https://github.com/crazy-max/yasu
Yet Another Switch User
https://github.com/crazy-max/yasu
exec gosu setgid setgroups setuid yasu
Last synced: 10 months ago
JSON representation
Yet Another Switch User
- Host: GitHub
- URL: https://github.com/crazy-max/yasu
- Owner: crazy-max
- License: apache-2.0
- Created: 2021-03-02T08:00:09.000Z (almost 5 years ago)
- Default Branch: master
- Last Pushed: 2024-09-05T06:12:02.000Z (over 1 year ago)
- Last Synced: 2024-10-18T07:32:22.603Z (over 1 year ago)
- Topics: exec, gosu, setgid, setgroups, setuid, yasu
- Language: Dockerfile
- Homepage:
- Size: 245 KB
- Stars: 18
- Watchers: 3
- Forks: 1
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: .github/CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Support: .github/SUPPORT.md
Awesome Lists containing this project
README
[](https://github.com/crazy-max/yasu/releases/latest)
[](https://github.com/crazy-max/yasu/releases/latest)
[](https://github.com/crazy-max/yasu/actions?query=workflow%3Abuild)
[](https://hub.docker.com/r/crazymax/yasu/)
[](https://hub.docker.com/r/crazymax/yasu/)
[](https://goreportcard.com/report/github.com/crazy-max/yasu)
___
* [Yet Another?](#yet-another)
* [About](#about)
* [Warning](#warning)
* [Usage](#usage)
* [From binary](#from-binary)
* [From Dockerfile](#from-dockerfile)
* [Build](#build)
* [Why?](#why)
* [Alternatives](#alternatives)
* [`su-exec`](#su-exec)
* [`chroot`](#chroot)
* [`setpriv`](#setpriv)
* [Others](#others)
* [Contributing](#contributing)
* [License](#license)
## Yet Another?
This repository is a fork of [tianon/gosu](https://github.com/tianon/gosu) and renamed to avoid confusion as asked by
the main maintainer. See [tianon/gosu#82 (comment)](https://github.com/tianon/gosu/pull/82#issuecomment-790874961).
`yasu` because it's _Yet Another Switch User_. The main purpose of this fork is to handle a functional
multi-platform scratch Docker image to ease the [integration in a Dockerfile](#from-dockerfile). Everything is
dockerized and handled by [buildx bake](#build) for an agnostic usage of this repo. Finally, GitHub Actions has been
added to avoid tampered artifacts and more transparency around [releases](https://github.com/crazy-max/yasu/releases).
More info: [tianon/gosu#82](https://github.com/tianon/gosu/pull/82)
## About
This is a simple tool grown out of the simple fact that `su` and `sudo` have very strange and often annoying TTY and
signal-forwarding behavior. They're also somewhat complex to setup and use (especially in the case of `sudo`), which
allows for a great deal of expressivity, but falls flat if all you need is "run this specific application as this
specific user and get out of the pipeline".
The core of how `yasu` works is stolen directly from how Docker/libcontainer itself starts an application inside a
container (and in fact, is using the `/etc/passwd` processing code directly from libcontainer's codebase).
```shell
$ yasu
Usage: ./yasu user-spec command [args]
eg: ./yasu tianon bash
./yasu nobody:root bash -c 'whoami && id'
./yasu 1000:1 id
./yasu version: 1.1 (go1.3.1 on linux/amd64; gc)
```
Once the user/group is processed, we switch to that user, then we `exec` the specified process and `yasu` itself is no
longer resident or involved in the process lifecycle at all. This avoids all the issues of signal passing and TTY,
and punts them to the process invoking `yasu` and the process being invoked by `yasu`, where they belong.
### Warning
The core use case for `yasu` is to step _down_ from `root` to a non-privileged user during container startup
(specifically in the `ENTRYPOINT`, usually).
Uses of `yasu` beyond that could very well suffer from vulnerabilities such as CVE-2016-2779 (from which the Docker
use case naturally shields us); see [`tianon/gosu#37`](https://github.com/tianon/gosu/issues/37) for some discussion
around this point.
## Usage
### From binary
`yasu` binaries are available on [releases page](https://github.com/crazy-max/yasu/releases/latest).
Choose the archive matching the destination platform:
```shell
wget -qO- https://github.com/crazy-max/yasu/releases/download/v1.13.0/yasu_1.13.0_linux_x86_64.tar.gz | tar -zxvf - yasu
yasu --version
yasu nobody true
```
### From Dockerfile
| Registry | Image |
|--------------------------------------------------------------------------------------------------|---------------------------------|
| [Docker Hub](https://hub.docker.com/r/crazymax/yasu/) | `crazymax/yasu` |
| [GitHub Container Registry](https://github.com/users/crazy-max/packages/container/package/yasu) | `ghcr.io/crazy-max/yasu` |
Following platforms for this image are available:
```
$ docker buildx imagetools inspect crazymax/yasu --format "{{json .Manifest}}" | \
jq -r '.manifests[] | select(.platform.os != null and .platform.os != "unknown") | .platform | "\(.os)/\(.architecture)\(if .variant then "/" + .variant else "" end)"'
linux/386
linux/amd64
linux/arm/v5
linux/arm/v6
linux/arm/v7
linux/arm64
linux/mips64le
linux/ppc64le
linux/riscv64
linux/s390x
```
Here is how to use `yasu` inside your Dockerfile:
```Dockerfile
FROM crazymax/yasu:latest AS yasu
FROM alpine
COPY --from=yasu / /
RUN yasu --version
RUN yasu nobody true
```
## Build
```shell
git clone https://github.com/crazy-max/yasu.git yasu
cd yasu
# validate (lint, vendors)
docker buildx bake validate
# test (test-alpine and test-debian bake targets)
docker buildx bake test
# build docker image and output to docker with yasu:local tag (default)
docker buildx bake
# build multi-platform image
docker buildx bake image-all
# build artifacts and output to ./bin/artifact
docker buildx bake artifact-all
```
## Why?
```shell
$ docker run -it --rm ubuntu:trusty su -c 'exec ps aux'
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 46636 2688 ? Ss+ 02:22 0:00 su -c exec ps a
root 6 0.0 0.0 15576 2220 ? Rs 02:22 0:00 ps aux
$ docker run -it --rm ubuntu:trusty sudo ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 3.0 0.0 46020 3144 ? Ss+ 02:22 0:00 sudo ps aux
root 7 0.0 0.0 15576 2172 ? R+ 02:22 0:00 ps aux
$ docker run -it --rm -v $PWD/yasu-amd64:/usr/local/bin/yasu:ro ubuntu:trusty yasu root ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 7140 768 ? Rs+ 02:22 0:00 ps aux
```
Additionally, due to the fact that `yasu` is using Docker's own code for processing these `user:group`, it has
exact 1:1 parity with Docker's own `--user` flag.
If you're curious about the edge cases that `yasu` handles, see [`hack/test.sh`](hack/test.sh) for the "test suite".
(Note that `sudo` has different goals from this project, and it is *not* intended to be a `sudo` replacement;
for example, see [this Stack Overflow answer](https://stackoverflow.com/a/48105623) for a short explanation of
why `sudo` does `fork`+`exec` instead of just `exec`.)
## Alternatives
### `su-exec`
As mentioned in `INSTALL.md`, [`su-exec`](https://github.com/ncopa/su-exec) is a very minimal re-write of `yasu` in C,
making for a much smaller binary, and is available in the `main` Alpine package repository.
### `chroot`
With the `--userspec` flag, `chroot` can provide similar benefits/behavior:
```shell
$ docker run -it --rm ubuntu:trusty chroot --userspec=nobody / ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
nobody 1 5.0 0.0 7136 756 ? Rs+ 17:04 0:00 ps aux
```
### `setpriv`
Available in newer `util-linux` (`>= 2.32.1-0.2`, in Debian; https://manpages.debian.org/buster/util-linux/setpriv.1.en.html):
```shell
$ docker run -it --rm buildpack-deps:buster-scm setpriv --reuid=nobody --regid=nogroup --init-groups ps faux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
nobody 1 5.0 0.0 9592 1252 pts/0 RNs+ 23:21 0:00 ps faux
```
### Others
I'm not terribly familiar with them, but a few other alternatives I'm aware of include:
* `chpst` (part of `runit`)
## Contributing
Want to contribute? Awesome! The most basic way to show your support is to star
the project, or to raise issues. You can also support this project by [**becoming a sponsor on GitHub**](https://github.com/sponsors/crazy-max)
or by making a [PayPal donation](https://www.paypal.me/crazyws) to ensure this
journey continues indefinitely!
Thanks again for your support, it is much appreciated! :pray:
## License
Apache-2.0. See `LICENSE` for more details.