{"id":15664028,"url":"https://github.com/crazy-max/yasu","last_synced_at":"2025-04-23T22:42:36.386Z","repository":{"id":37240427,"uuid":"343691497","full_name":"crazy-max/yasu","owner":"crazy-max","description":"Yet Another Switch User","archived":false,"fork":false,"pushed_at":"2024-09-05T06:12:02.000Z","size":251,"stargazers_count":18,"open_issues_count":2,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-18T07:32:22.603Z","etag":null,"topics":["exec","gosu","setgid","setgroups","setuid","yasu"],"latest_commit_sha":null,"homepage":"","language":"Dockerfile","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/crazy-max.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":".github/SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"crazy-max","custom":"https://www.paypal.me/crazyws"}},"created_at":"2021-03-02T08:00:09.000Z","updated_at":"2024-09-10T00:58:46.000Z","dependencies_parsed_at":"2023-12-21T20:16:21.076Z","dependency_job_id":"65889007-5fdf-41f8-b342-231bee6f8cc1","html_url":"https://github.com/crazy-max/yasu","commit_stats":{"total_commits":190,"total_committers":15,"mean_commits":"12.666666666666666","dds":0.4526315789473684,"last_synced_commit":"7d8a73b1236535458d533bb2228d1b0247ea72f9"},"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crazy-max%2Fyasu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crazy-max%2Fyasu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crazy-max%2Fyasu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crazy-max%2Fyasu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/crazy-max","download_url":"https://codeload.github.com/crazy-max/yasu/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250528681,"owners_count":21445511,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["exec","gosu","setgid","setgroups","setuid","yasu"],"created_at":"2024-10-03T13:40:57.256Z","updated_at":"2025-04-23T22:42:36.358Z","avatar_url":"https://github.com/crazy-max.png","language":"Dockerfile","funding_links":["https://github.com/sponsors/crazy-max","https://www.paypal.me/crazyws"],"categories":[],"sub_categories":[],"readme":"[![GitHub release](https://img.shields.io/github/release/crazy-max/yasu.svg?style=flat-square)](https://github.com/crazy-max/yasu/releases/latest)\n[![Total downloads](https://img.shields.io/github/downloads/crazy-max/yasu/total.svg?style=flat-square)](https://github.com/crazy-max/yasu/releases/latest)\n[![Build Status](https://img.shields.io/github/actions/workflow/status/crazy-max/yasu/build.yml?branch=master\u0026label=build\u0026logo=github\u0026style=flat-square)](https://github.com/crazy-max/yasu/actions?query=workflow%3Abuild)\n[![Docker Stars](https://img.shields.io/docker/stars/crazymax/yasu.svg?style=flat-square\u0026logo=docker)](https://hub.docker.com/r/crazymax/yasu/)\n[![Docker Pulls](https://img.shields.io/docker/pulls/crazymax/yasu.svg?style=flat-square\u0026logo=docker)](https://hub.docker.com/r/crazymax/yasu/)\n[![Go Report Card](https://goreportcard.com/badge/github.com/crazy-max/yasu)](https://goreportcard.com/report/github.com/crazy-max/yasu)\n\n___\n\n* [Yet Another?](#yet-another)\n* [About](#about)\n  * [Warning](#warning)\n* [Usage](#usage)\n  * [From binary](#from-binary)\n  * [From Dockerfile](#from-dockerfile)\n* [Build](#build)\n* [Why?](#why)\n* [Alternatives](#alternatives)\n  * [`su-exec`](#su-exec)\n  * [`chroot`](#chroot)\n  * [`setpriv`](#setpriv)\n  * [Others](#others)\n* [Contributing](#contributing)\n* [License](#license)\n\n## Yet Another?\n\nThis repository is a fork of [tianon/gosu](https://github.com/tianon/gosu) and renamed to avoid confusion as asked by\nthe main maintainer. See [tianon/gosu#82 (comment)](https://github.com/tianon/gosu/pull/82#issuecomment-790874961).\n\n`yasu` because it's _Yet Another Switch User_. The main purpose of this fork is to handle a functional\nmulti-platform scratch Docker image to ease the [integration in a Dockerfile](#from-dockerfile). Everything is\ndockerized and handled by [buildx bake](#build) for an agnostic usage of this repo. Finally, GitHub Actions has been\nadded to avoid tampered artifacts and more transparency around [releases](https://github.com/crazy-max/yasu/releases).\n\nMore info: [tianon/gosu#82](https://github.com/tianon/gosu/pull/82)\n\n## About\n\nThis is a simple tool grown out of the simple fact that `su` and `sudo` have very strange and often annoying TTY and\nsignal-forwarding behavior. They're also somewhat complex to setup and use (especially in the case of `sudo`), which\nallows for a great deal of expressivity, but falls flat if all you need is \"run this specific application as this\nspecific user and get out of the pipeline\".\n\nThe core of how `yasu` works is stolen directly from how Docker/libcontainer itself starts an application inside a\ncontainer (and in fact, is using the `/etc/passwd` processing code directly from libcontainer's codebase).\n\n```shell\n$ yasu\nUsage: ./yasu user-spec command [args]\n   eg: ./yasu tianon bash\n       ./yasu nobody:root bash -c 'whoami \u0026\u0026 id'\n       ./yasu 1000:1 id\n\n./yasu version: 1.1 (go1.3.1 on linux/amd64; gc)\n```\n\nOnce the user/group is processed, we switch to that user, then we `exec` the specified process and `yasu` itself is no\nlonger resident or involved in the process lifecycle at all.  This avoids all the issues of signal passing and TTY,\nand punts them to the process invoking `yasu` and the process being invoked by `yasu`, where they belong.\n\n### Warning\n\nThe core use case for `yasu` is to step _down_ from `root` to a non-privileged user during container startup\n(specifically in the `ENTRYPOINT`, usually).\n\nUses of `yasu` beyond that could very well suffer from vulnerabilities such as CVE-2016-2779 (from which the Docker\nuse case naturally shields us); see [`tianon/gosu#37`](https://github.com/tianon/gosu/issues/37) for some discussion\naround this point.\n\n## Usage\n\n### From binary\n\n`yasu` binaries are available on [releases page](https://github.com/crazy-max/yasu/releases/latest).\n\nChoose the archive matching the destination platform:\n\n```shell\nwget -qO- https://github.com/crazy-max/yasu/releases/download/v1.13.0/yasu_1.13.0_linux_x86_64.tar.gz | tar -zxvf - yasu\nyasu --version\nyasu nobody true\n```\n\n### From Dockerfile\n\n| Registry                                                                                         | Image                           |\n|--------------------------------------------------------------------------------------------------|---------------------------------|\n| [Docker Hub](https://hub.docker.com/r/crazymax/yasu/)                                            | `crazymax/yasu`                 |\n| [GitHub Container Registry](https://github.com/users/crazy-max/packages/container/package/yasu)  | `ghcr.io/crazy-max/yasu`        |\n\nFollowing platforms for this image are available:\n\n```\n$ docker buildx imagetools inspect crazymax/yasu --format \"{{json .Manifest}}\" | \\\n  jq -r '.manifests[] | select(.platform.os != null and .platform.os != \"unknown\") | .platform | \"\\(.os)/\\(.architecture)\\(if .variant then \"/\" + .variant else \"\" end)\"'\n\nlinux/386\nlinux/amd64\nlinux/arm/v5\nlinux/arm/v6\nlinux/arm/v7\nlinux/arm64\nlinux/mips64le\nlinux/ppc64le\nlinux/riscv64\nlinux/s390x\n```\n\nHere is how to use `yasu` inside your Dockerfile:\n\n```Dockerfile\nFROM crazymax/yasu:latest AS yasu\nFROM alpine\nCOPY --from=yasu / /\nRUN yasu --version\nRUN yasu nobody true\n```\n\n## Build\n\n```shell\ngit clone https://github.com/crazy-max/yasu.git yasu\ncd yasu\n\n# validate (lint, vendors)\ndocker buildx bake validate\n\n# test (test-alpine and test-debian bake targets)\ndocker buildx bake test\n\n# build docker image and output to docker with yasu:local tag (default)\ndocker buildx bake\n\n# build multi-platform image\ndocker buildx bake image-all\n\n# build artifacts and output to ./bin/artifact\ndocker buildx bake artifact-all\n```\n\n## Why?\n\n```shell\n$ docker run -it --rm ubuntu:trusty su -c 'exec ps aux'\nUSER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND\nroot         1  0.0  0.0  46636  2688 ?        Ss+  02:22   0:00 su -c exec ps a\nroot         6  0.0  0.0  15576  2220 ?        Rs   02:22   0:00 ps aux\n$ docker run -it --rm ubuntu:trusty sudo ps aux\nUSER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND\nroot         1  3.0  0.0  46020  3144 ?        Ss+  02:22   0:00 sudo ps aux\nroot         7  0.0  0.0  15576  2172 ?        R+   02:22   0:00 ps aux\n$ docker run -it --rm -v $PWD/yasu-amd64:/usr/local/bin/yasu:ro ubuntu:trusty yasu root ps aux\nUSER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND\nroot         1  0.0  0.0   7140   768 ?        Rs+  02:22   0:00 ps aux\n```\n\nAdditionally, due to the fact that `yasu` is using Docker's own code for processing these `user:group`, it has\nexact 1:1 parity with Docker's own `--user` flag.\n\nIf you're curious about the edge cases that `yasu` handles, see [`hack/test.sh`](hack/test.sh) for the \"test suite\".\n\n(Note that `sudo` has different goals from this project, and it is *not* intended to be a `sudo` replacement;\nfor example, see [this Stack Overflow answer](https://stackoverflow.com/a/48105623) for a short explanation of\nwhy `sudo` does `fork`+`exec` instead of just `exec`.)\n\n## Alternatives\n\n### `su-exec`\n\nAs mentioned in `INSTALL.md`, [`su-exec`](https://github.com/ncopa/su-exec) is a very minimal re-write of `yasu` in C,\nmaking for a much smaller binary, and is available in the `main` Alpine package repository.\n\n### `chroot`\n\nWith the `--userspec` flag, `chroot` can provide similar benefits/behavior:\n\n```shell\n$ docker run -it --rm ubuntu:trusty chroot --userspec=nobody / ps aux\nUSER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND\nnobody       1  5.0  0.0   7136   756 ?        Rs+  17:04   0:00 ps aux\n```\n\n### `setpriv`\n\nAvailable in newer `util-linux` (`\u003e= 2.32.1-0.2`, in Debian; https://manpages.debian.org/buster/util-linux/setpriv.1.en.html):\n\n```shell\n$ docker run -it --rm buildpack-deps:buster-scm setpriv --reuid=nobody --regid=nogroup --init-groups ps faux\nUSER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND\nnobody       1  5.0  0.0   9592  1252 pts/0    RNs+ 23:21   0:00 ps faux\n```\n\n### Others\n\nI'm not terribly familiar with them, but a few other alternatives I'm aware of include:\n\n* `chpst` (part of `runit`)\n\n## Contributing\n\nWant to contribute? Awesome! The most basic way to show your support is to star\nthe project, or to raise issues. You can also support this project by [**becoming a sponsor on GitHub**](https://github.com/sponsors/crazy-max)\nor by making a [PayPal donation](https://www.paypal.me/crazyws) to ensure this\njourney continues indefinitely!\n\nThanks again for your support, it is much appreciated! :pray:\n\n## License\n\nApache-2.0. See `LICENSE` for more details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrazy-max%2Fyasu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcrazy-max%2Fyasu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrazy-max%2Fyasu/lists"}