mirror of
https://git.mirrors.martin98.com/https://github.com/ceph/ceph-csi.git
synced 2025-08-20 21:09:07 +08:00

Several packages are only used while running the e2e suite. These packages are less important to update, as the they can not influence the final executable that is part of the Ceph-CSI container-image. By moving these dependencies out of the main Ceph-CSI go.mod, it is easier to identify if a reported CVE affects Ceph-CSI, or only the testing (like most of the Kubernetes CVEs). Signed-off-by: Niels de Vos <ndevos@ibm.com>
170 lines
7.1 KiB
Markdown
170 lines
7.1 KiB
Markdown
## `filepath-securejoin` ##
|
|
|
|
[](https://pkg.go.dev/github.com/cyphar/filepath-securejoin)
|
|
[](https://github.com/cyphar/filepath-securejoin/actions/workflows/ci.yml)
|
|
|
|
### Old API ###
|
|
|
|
This library was originally just an implementation of `SecureJoin` which was
|
|
[intended to be included in the Go standard library][go#20126] as a safer
|
|
`filepath.Join` that would restrict the path lookup to be inside a root
|
|
directory.
|
|
|
|
The implementation was based on code that existed in several container
|
|
runtimes. Unfortunately, this API is **fundamentally unsafe** against attackers
|
|
that can modify path components after `SecureJoin` returns and before the
|
|
caller uses the path, allowing for some fairly trivial TOCTOU attacks.
|
|
|
|
`SecureJoin` (and `SecureJoinVFS`) are still provided by this library to
|
|
support legacy users, but new users are strongly suggested to avoid using
|
|
`SecureJoin` and instead use the [new api](#new-api) or switch to
|
|
[libpathrs][libpathrs].
|
|
|
|
With the above limitations in mind, this library guarantees the following:
|
|
|
|
* If no error is set, the resulting string **must** be a child path of
|
|
`root` and will not contain any symlink path components (they will all be
|
|
expanded).
|
|
|
|
* When expanding symlinks, all symlink path components **must** be resolved
|
|
relative to the provided root. In particular, this can be considered a
|
|
userspace implementation of how `chroot(2)` operates on file paths. Note that
|
|
these symlinks will **not** be expanded lexically (`filepath.Clean` is not
|
|
called on the input before processing).
|
|
|
|
* Non-existent path components are unaffected by `SecureJoin` (similar to
|
|
`filepath.EvalSymlinks`'s semantics).
|
|
|
|
* The returned path will always be `filepath.Clean`ed and thus not contain any
|
|
`..` components.
|
|
|
|
A (trivial) implementation of this function on GNU/Linux systems could be done
|
|
with the following (note that this requires root privileges and is far more
|
|
opaque than the implementation in this library, and also requires that
|
|
`readlink` is inside the `root` path and is trustworthy):
|
|
|
|
```go
|
|
package securejoin
|
|
|
|
import (
|
|
"os/exec"
|
|
"path/filepath"
|
|
)
|
|
|
|
func SecureJoin(root, unsafePath string) (string, error) {
|
|
unsafePath = string(filepath.Separator) + unsafePath
|
|
cmd := exec.Command("chroot", root,
|
|
"readlink", "--canonicalize-missing", "--no-newline", unsafePath)
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
expanded := string(output)
|
|
return filepath.Join(root, expanded), nil
|
|
}
|
|
```
|
|
|
|
[libpathrs]: https://github.com/openSUSE/libpathrs
|
|
[go#20126]: https://github.com/golang/go/issues/20126
|
|
|
|
### New API ###
|
|
|
|
While we recommend users switch to [libpathrs][libpathrs] as soon as it has a
|
|
stable release, some methods implemented by libpathrs have been ported to this
|
|
library to ease the transition. These APIs are only supported on Linux.
|
|
|
|
These APIs are implemented such that `filepath-securejoin` will
|
|
opportunistically use certain newer kernel APIs that make these operations far
|
|
more secure. In particular:
|
|
|
|
* All of the lookup operations will use [`openat2`][openat2.2] on new enough
|
|
kernels (Linux 5.6 or later) to restrict lookups through magic-links and
|
|
bind-mounts (for certain operations) and to make use of `RESOLVE_IN_ROOT` to
|
|
efficiently resolve symlinks within a rootfs.
|
|
|
|
* The APIs provide hardening against a malicious `/proc` mount to either detect
|
|
or avoid being tricked by a `/proc` that is not legitimate. This is done
|
|
using [`openat2`][openat2.2] for all users, and privileged users will also be
|
|
further protected by using [`fsopen`][fsopen.2] and [`open_tree`][open_tree.2]
|
|
(Linux 5.2 or later).
|
|
|
|
[openat2.2]: https://www.man7.org/linux/man-pages/man2/openat2.2.html
|
|
[fsopen.2]: https://github.com/brauner/man-pages-md/blob/main/fsopen.md
|
|
[open_tree.2]: https://github.com/brauner/man-pages-md/blob/main/open_tree.md
|
|
|
|
#### `OpenInRoot` ####
|
|
|
|
```go
|
|
func OpenInRoot(root, unsafePath string) (*os.File, error)
|
|
func OpenatInRoot(root *os.File, unsafePath string) (*os.File, error)
|
|
func Reopen(handle *os.File, flags int) (*os.File, error)
|
|
```
|
|
|
|
`OpenInRoot` is a much safer version of
|
|
|
|
```go
|
|
path, err := securejoin.SecureJoin(root, unsafePath)
|
|
file, err := os.OpenFile(path, unix.O_PATH|unix.O_CLOEXEC)
|
|
```
|
|
|
|
that protects against various race attacks that could lead to serious security
|
|
issues, depending on the application. Note that the returned `*os.File` is an
|
|
`O_PATH` file descriptor, which is quite restricted. Callers will probably need
|
|
to use `Reopen` to get a more usable handle (this split is done to provide
|
|
useful features like PTY spawning and to avoid users accidentally opening bad
|
|
inodes that could cause a DoS).
|
|
|
|
Callers need to be careful in how they use the returned `*os.File`. Usually it
|
|
is only safe to operate on the handle directly, and it is very easy to create a
|
|
security issue. [libpathrs][libpathrs] provides far more helpers to make using
|
|
these handles safer -- there is currently no plan to port them to
|
|
`filepath-securejoin`.
|
|
|
|
`OpenatInRoot` is like `OpenInRoot` except that the root is provided using an
|
|
`*os.File`. This allows you to ensure that multiple `OpenatInRoot` (or
|
|
`MkdirAllHandle`) calls are operating on the same rootfs.
|
|
|
|
> **NOTE**: Unlike `SecureJoin`, `OpenInRoot` will error out as soon as it hits
|
|
> a dangling symlink or non-existent path. This is in contrast to `SecureJoin`
|
|
> which treated non-existent components as though they were real directories,
|
|
> and would allow for partial resolution of dangling symlinks. These behaviours
|
|
> are at odds with how Linux treats non-existent paths and dangling symlinks,
|
|
> and so these are no longer allowed.
|
|
|
|
#### `MkdirAll` ####
|
|
|
|
```go
|
|
func MkdirAll(root, unsafePath string, mode int) error
|
|
func MkdirAllHandle(root *os.File, unsafePath string, mode int) (*os.File, error)
|
|
```
|
|
|
|
`MkdirAll` is a much safer version of
|
|
|
|
```go
|
|
path, err := securejoin.SecureJoin(root, unsafePath)
|
|
err = os.MkdirAll(path, mode)
|
|
```
|
|
|
|
that protects against the same kinds of races that `OpenInRoot` protects
|
|
against.
|
|
|
|
`MkdirAllHandle` is like `MkdirAll` except that the root is provided using an
|
|
`*os.File` (the reason for this is the same as with `OpenatInRoot`) and an
|
|
`*os.File` of the final created directory is returned (this directory is
|
|
guaranteed to be effectively identical to the directory created by
|
|
`MkdirAllHandle`, which is not possible to ensure by just using `OpenatInRoot`
|
|
after `MkdirAll`).
|
|
|
|
> **NOTE**: Unlike `SecureJoin`, `MkdirAll` will error out as soon as it hits
|
|
> a dangling symlink or non-existent path. This is in contrast to `SecureJoin`
|
|
> which treated non-existent components as though they were real directories,
|
|
> and would allow for partial resolution of dangling symlinks. These behaviours
|
|
> are at odds with how Linux treats non-existent paths and dangling symlinks,
|
|
> and so these are no longer allowed. This means that `MkdirAll` will not
|
|
> create non-existent directories referenced by a dangling symlink.
|
|
|
|
### License ###
|
|
|
|
The license of this project is the same as Go, which is a BSD 3-clause license
|
|
available in the `LICENSE` file.
|