This repository is a fork of tianon/gosu and renamed to avoid confusion as asked by the main maintainer. See tianon/gosu#82 (comment).
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. Everything is
dockerized and handled by buildx bake for an agnostic usage of this repo. Finally, GitHub Actions has been
added to avoid tampered artifacts and more transparency around releases.
More info: tianon/gosu#82
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).
$ 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.
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
for some discussion
around this point.
yasu
binaries are available on releases page.
Choose the archive matching the destination platform:
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
Registry | Image |
---|---|
Docker Hub | crazymax/yasu |
GitHub Container Registry | ghcr.io/crazy-max/yasu |
Following platforms for this image are available:
$ docker run --rm mplatform/mquery crazymax/yasu:latest
Image: crazymax/yasu:latest
* Manifest List: Yes
* Supported platforms:
- 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:
FROM crazymax/yasu:latest AS yasu
FROM alpine
COPY --from=yasu / /
RUN yasu --version
RUN yasu nobody true
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
$ 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
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 for a short explanation of
why sudo
does fork
+exec
instead of just exec
.)
As mentioned in INSTALL.md
, 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.
With the --userspec
flag, chroot
can provide similar benefits/behavior:
$ 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
Available in newer util-linux
(>= 2.32.1-0.2
, in Debian; https://manpages.debian.org/buster/util-linux/setpriv.1.en.html):
$ 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
I'm not terribly familiar with them, but a few other alternatives I'm aware of include:
chpst
(part ofrunit
)
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 or by making a PayPal donation to ensure this journey continues indefinitely!
Thanks again for your support, it is much appreciated! 🙏
Apache-2.0. See LICENSE
for more details.