Simple Go-based setuid+setgid+setgroups+exec

gosu

Build Status

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 gosu 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).

$ gosu
Usage: ./gosu user-spec command [args]
   eg: ./gosu tianon bash
       ./gosu nobody:root bash -c 'whoami && id'
       ./gosu 1000:1 id

./gosu 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 gosu 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 gosu and the process being invoked by gosu, where they belong.

Warning

The core use case for gosu is to step down from root to a non-privileged user during container startup (specifically in the ENTRYPOINT, usually).

Uses of gosu 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.

Installation

High-level steps:

  1. download gosu-$(dpkg --print-architecture | awk -F- '{ print $NF }') as gosu
  2. download gosu-$(dpkg --print-architecture | awk -F- '{ print $NF }').asc as gosu.asc
  3. fetch my public key (to verify your download): gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4
  4. gpg --batch --verify gosu.asc gosu
  5. chmod +x gosu

For explicit Dockerfile instructions, see INSTALL.md.

Why?

$ 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/gosu-amd64:/usr/local/bin/gosu:ro ubuntu:trusty gosu 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 gosu 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 gosu handles, see Dockerfile.test for the "test suite" (and the associated test.sh script that wraps this up for testing arbitrary binaries).

(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.)

Alternatives

su-exec

As mentioned in INSTALL.md, su-exec is a very minimal re-write of gosu 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:

$ 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):

$ 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)
Owner
Tianon Gravi
bashochist; debian, dragon, father, go, perl, tron, vim; basically nine years old; he/him or they/them
Tianon Gravi
Comments
  • Revert

    Revert "Adjust SetupUser to Fchown all open file descriptors so they'…

    …re appropriately usable by the forked process"

    This reverts commit 75150acc2b2baf01a8ce5b106f226464f3c737e0.

    There's no guarantee that gosu will be able to change the owner of open files. Consequently, in some environments, this causes gosu to fail with the error:

    error: failed switching to "$user": fchown fds permission denied
    

    For some examples of failures people have encountered, see https://github.com/docker-library/mongo/issues/56

    Any open file descriptors may not even be files, or they may not be on a filesystem that supports changing owners, or even has any concept of owners (FAT16, for example).

    File descriptors may not even be files. They may be sockets, for example.

    This behavior is also quite surprising. I would not expect a utility that behaves something like sudo to modify my filesystem. Many times, I may explicitly not want a file to be owned by the target user because I want the process I'm launching to be restricted to read-only access. Consequently, this behavior could introduce security issues for some use cases.

  • B42F6819007F00F88E364FD4036A9C25BF357DD4 key not found

    B42F6819007F00F88E364FD4036A9C25BF357DD4 key not found

    https://gowalker.org/github.com/tianon/gosu

    FROM debian

    ENV GOSU_VERSION 1.7 RUN set -x
    When I run, its not working .. because ..

    && gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \
    

    http://ha.pool.sks-keyservers.net/ This key is not existing as per this website as well.

    Can you please give a solution? Thanks in advance.

  • Gosu 1.8 binary release doesnt match architecture

    Gosu 1.8 binary release doesnt match architecture

    Just realised when upgrade the gosu binary in the Gogs Docker Container that the current binary release for the armhf architecture is broken. It seems that the current gosu-armhf binary is exactly the same binary as gosu-amd64.

    gosu binary file report gosu armor check

  • CVEs that do not apply to gosu

    CVEs that do not apply to gosu

    CVEs that do not apply to builds of gosu:

    If you use (or maintain) a security scanner which reports any of these against gosu, please report them to the security vendor as false positives.

    (See also https://snarky.ca/the-social-contract-of-open-source/)

  • Relicense

    Relicense

    I've been thinking for a long time about re-licensing this project from GPL-3 to Apache-2.0 (which is probably more accurate/correct anyhow, given that what it imports from is all Apache-2.0).

    This project being GPL-licensed has been a barrier for some users trying to convince their organizations that it's OK to use, and I don't think the terms of the GPL have really done anything for this project (and I've read many similar accounts from other projects -- the success stories are far outpaced :disappointed:).

    I'm creating this issue to have a place to ping all my wonderful contributors to see if there's any dissent in doing so (and I'd appreciate anyone who's in this list commenting with a "yes" or "no" explicitly - hopefully if you're against this you can provide a little bit of a reason why :smile:). I've also tried to include a link to a PR of yours that I've merged in case you don't remember contributing. :sweat_smile:

    • [x] @bitglue (#8)
    • [x] @chenrui333 (#67)
    • [x] @elsmorian (#23)
    • [x] @JayH5 (#25)
    • [x] @kiranmantri (#28)
    • [x] @NanXiao (#12)
    • [ ] @sebastianwebber (#32)
    • [ ] @temtemy (#48)
    • [x] @tianon :innocent:
    • [x] @xyproto (#7)
    • [x] @ydcool (#69)

    I also want to express heartfelt thanks to each of you for contributing to whatever it is I've got going on here!! The meat of this tool is ~80 lines of Go, most of which are comments, whitespace, and boilerplate, so I love that so many folks wanted to and have found ways to contribute in spite of that! Y'all are awesome. :heart:

  • Does gosu handle the case where the user doesn't exist in the host /etc/passwd due to LDAP/AD bindings?

    Does gosu handle the case where the user doesn't exist in the host /etc/passwd due to LDAP/AD bindings?

    I've been looking for a way to not muck up permissions when mounting files for userland programs run from Docker containers (ie Github-Hub) but every time I find something that seems useful, it fails in an epic manner when I try it on my work laptop where I don't exist in the local /etc/passwd. Since I'm only on the system via sssd/nsswitch for getent calls, there is no record of my user elsewhere that I've found.

  • HTTP fetch error 60: Peer certificate cannot be authenticated with known CA certificates

    HTTP fetch error 60: Peer certificate cannot be authenticated with known CA certificates

    gpgkeys throws an http fetch error for keyserver hkps://keys.openpgp.org

    + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4
    gpg: directory `/root/.gnupg' created
    gpg: new configuration file `/root/.gnupg/gpg.conf' created
    gpg: WARNING: options in `/root/.gnupg/gpg.conf' are not yet active during this run
    gpg: keyring `/root/.gnupg/secring.gpg' created
    gpg: keyring `/root/.gnupg/pubring.gpg' created
    gpg: requesting key BF357DD4 from hkps server keys.openpgp.org
    gpgkeys: HTTP fetch error 60: Peer certificate cannot be authenticated with known CA certificates
    gpg: no valid OpenPGP data found.
    gpg: Total number processed: 0
    
  • GOSU fails to switch when provided user_id is larger than 65536?!!

    GOSU fails to switch when provided user_id is larger than 65536?!!

    I am using gosu within a docker container that is going to be run within a rootless docker mode. The rootless mode is required.

    I am trying to use gosu when launching the container to maintain the file permissions and ownership to the host user for files generated during a docker session.

    The problem is whenever I try to invoke gosu to switch to the newly added user within docker (that matches the host user), I get the following errro:

    error: failed switching to "some_user": invalid argument
    

    I looked around and found that this might the most related issue: #64 However, when I try what was the developer who asked the question struggling with, I didn't have a problem executing the command:

    gosu 1000 id # or gosu 1000:1000 id 
    

    That being the case I did several tests and found out that there seems to be an upper limit on the user id that gosu accepts:

    $ gosu 65536 id
    uid=65536 gid=0(root) groups=0(root)
    $ gosu 65537 id
    error: failed switching to "65537": invalid argument
    

    Unfortunately, my user id has a value larger than 65536. Can some one explain why is this a problem and whether there is a way around this limitation.

    $ gosu --version
    gosu version: 1.10 (go1.10.4 on linux/amd64; gc)
         license: GPL-3 (full text at https://github.com/tianon/gosu)
    
  • user switching failing with user namespace mapping

    user switching failing with user namespace mapping

    Hi all,

    gosu fails to switch the user in a Docker container when running dockerd with user namespace mapping enabled. While without user namespaces switching the user context works fine, it fails with

    [root@ffb6964e14f4 /]# gosu testuser id
    error: failed switching to "testuser": invalid argument
    

    I am not sure if it is actually a gosu issue or a more general limitation?

    setup

    • gosu 1.11
    • Docker: 19.03.1, build 74b1e89
    • /etc/sub{uid,gid} : dockeruser:120000:10000
    • /etc/docker/daemon.json : { "userns-remap": "dockeruser" }

    Dockerfile snippet

    ARG USERID=26551
    ENV runUID=${USERID}
    ARG GROUPID=26551
    ENV runGID=${GROUPID}
    ARG USERNAME='testuser'
    ENV runUSER=${USERNAME}
    ARG groupNAME='testgroup'
    ENV runGROUP=${groupNAME}
    RUN groupadd -g ${runGID}  ${runGROUP} && \
    useradd -u ${runUID} -g ${runGID} -r ${runUSER} 
    
    ENV GOSU_VERSION="1.11" 
    ENV GOSU_DOWNLOAD_ROOT="https://github.com/tianon/gosu/releases/download/$GOSU_VERSION" 
    ENV GOSU_DOWNLOAD_KEY="0x036A9C25BF357DD4"
    
    ENV GOSU_ENTRYPOINT_VERSION="1.0.0"
    ENV GOSU_ENTRYPOINT_DOWNLOAD="https://github.com/gisjedi/gosu-entrypoint/releases/download/$GOSU_ENTRYPOINT_VERSION/gosu-entrypoint.sh" 
    ENV GOSU_USER ${runUID}:${runGID}
    
    RUN set -x;  gosu ${runUSER} /usr/bin/whoami ; gosu ${runUSER} id
    

    build

    during build gosu can actually switch the user to the recently created user (see last line in the Dockerbuild snippet) - I guess, that build does not use user namespaces...

    > DOCKER_BUILDKIT=1  docker build --progress=plain  . -t gosu_test:latest
    ...
    #25 [16/16] RUN set -x;  gosu testuser /usr/bin/whoami ; gosu testuser id
    #25 0.557 + gosu testuser /usr/bin/whoami
    #25 0.570 testuser
    #25 0.571 + gosu testuser id
    #25 0.580 uid=26551(testuser) gid=26551(testgroup) groups=26551(testgroup)
    

    run

    with user namespaces active

    > docker run -it gosu_test:latest /bin/bash
    [root@9a57e45dc84f /]# gosu testuser id
    error: failed switching to "testuser": invalid argument
    

    disabling user namespaces

    > docker run --userns=host -it gosu_test:latest /bin/bash
    [root@1ea9006e9380 /]# gosu testuser id
    uid=26551(testuser) gid=26551(testgroup) groups=26551(testgroup)
    

    question

    Can I actually switch the user context in a container with user namespaces in use?

    According to the Docker documentation on User NS, binaries relying on setuid might not work.

    My suspicion is, that since gosu (or su for that matter) run by under owner '0' is mapped to some UID in /etc/subuid, the capability is not properly usable anymore??

    Cheers, Thomas

  • Error with gpg at ha.pool.sks-keyservers.net

    Error with gpg at ha.pool.sks-keyservers.net

    Hi, I am trying to install this:

    gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4

    its giving:

    • mktemp -d
    • export GNUPGHOME=/tmp/tmp.M9AXejR6yQ
    • gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 gpg: keybox '/tmp/tmp.M9AXejR6yQ/pubring.kbx' created gpg: keyserver receive failed: Connection timed out

    problem with server ? when I was used "gpg --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4" have the same problem.

  • The key is not certified with a trusted signature in Debian stretch

    The key is not certified with a trusted signature in Debian stretch

    How to reproduce Issue the following command on Debian 9 (stretch) official container as root user:

    apt-get update && apt-get install -y --no-install-recommends \
          gnupg \
          dirmngr \
        && dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')" \
        && wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.10/gosu-$dpkgArch" \
        && wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/1.10/gosu-$dpkgArch.asc" \
        && export GNUPGHOME="$(mktemp -d)" \
        && gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \
        && gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \
        && rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc \
        && chmod +x /usr/local/bin/gosu \
        && gosu nobody true
    

    As a result, an output similar to the following is expected:

    gpg: keybox '/tmp/tmp.J52ezIfr5l/pubring.kbx' created
    gpg: /tmp/tmp.J52ezIfr5l/trustdb.gpg: trustdb created
    gpg: key 036A9C25BF357DD4: public key "Tianon Gravi <[email protected]>" imported
    gpg: no ultimately trusted keys found
    gpg: Total number processed: 1
    gpg:               imported: 1
    gpg: Signature made Wed Sep 28 22:22:56 2016 UTC
    gpg:                using RSA key 036A9C25BF357DD4
    gpg: Good signature from "Tianon Gravi <[email protected]>" [unknown]
    gpg:                 aka "Tianon Gravi <[email protected]>" [unknown]
    gpg:                 aka "Tianon Gravi <[email protected]>" [unknown]
    gpg:                 aka "Andrew Page (tianon) <[email protected]>" [unknown]
    gpg:                 aka "Andrew Page (tianon) <[email protected]>" [unknown]
    gpg:                 aka "Andrew Page (Tianon Gravi) <[email protected]>" [unknown]
    gpg:                 aka "Tianon Gravi (Andrew Page) <[email protected]>" [unknown]
    gpg: WARNING: This key is not certified with a trusted signature!
    gpg:          There is no indication that the signature belongs to the owner.
    Primary key fingerprint: B42F 6819 007F 00F8 8E36  4FD4 036A 9C25 BF35 7DD4
    
  • Provide binaries compatible with `$(uname -m)`

    Provide binaries compatible with `$(uname -m)`

    Hi! gosu is not available as package on some distribution, RHEL7/8 for instance.

    Would you consider providing the binary in a compatible way with uname -m, or maybe provide an example on how to pull the binary on non-debian OS (where dpkg is not available)?

    For instance dumb-init project that is widely used in Docker context provide them with a different naming and the following works and is pretty standard on Linux to determine architecture: curl -sL "https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_$(uname -m)" >/usr/local/bin/dumb-init

    Of course this should be a non-breaking change for people already relaying on the actual naming, I am not sure if GitHub provide something for that. So maybe documenting somewhere how to pull it form GH on non-debian OS is a good first step.

    Cheers!

  • Keeping the TTY across a privilege boundary might be insecure

    Keeping the TTY across a privilege boundary might be insecure

    As per IRC:

    <+TimWolla> tianon, you might want to check whether this applies to gosu: http://www.openwall.com/lists/oss-security/2017/06/03/9 <+TimWolla> Should I create an issue? <@tianon> TimWolla: hrm, that's troubling -- we intentionally don't touch anything TTY related (that's one of the main features over "su" or "sudo") :( <@tianon> TimWolla: an issue would probably be great for discussing / figuring out whether there's impact to gosu and whether there's something we should fix or document about it :)

Related tags
Small CLI based programs for solving structural engineering design problems based on the book 'Structural Concrete'

Small CLI based programs for solving structural engineering design problems based on the book 'Structural Concrete' written by M. Nadim Hassoun and Akhtem Al-Manaseer (edition-6)

Nov 26, 2021
A simple command line tool using which you can skip phone number based SMS verification by using a temporary phone number that acts like a proxy.
A simple command line tool using which you can skip phone number based SMS verification by using a temporary phone number that acts like a proxy.

Fake-SMS A simple command line tool using which you can skip phone number based SMS verification by using a temporary phone number that acts like a pr

Dec 31, 2022
TScli - a very simple terminal-based client for TSWeb online judge

TScli TScli - a very simple terminal-based client for TSWeb online judge. It supports submitting problems and receiving feedback on them. Installation

Oct 24, 2021
A go lib based on tcell which builds a simple menu UI in your terminal.

MenuScreen A simple go lib based on github.com/gdamore/tcell/v2,which helps you build a simple menu UI in your terminal. Install go get -u github.com/

Nov 11, 2021
Browser based Ascii-art generator with simple web design

Browser based Ascii-art generator with simple web design

Oct 31, 2022
A dead simple CLI tool that prints the next semantic version based on the last tag of your git repository

nextver A dead simple CLI tool that prints the next semantic version based on the last tag of your git repository. Install go install github.com/junk1

Sep 29, 2022
Simple trie based auto-completion engine implementation in golang.
Simple trie based auto-completion engine implementation in golang.

Simple auto-complete engine implementation in golang. Quick start $ git clone https://github.com/benbarron/trie-auto-completion-engine $ cd trie-auto-

Jul 12, 2022
Golang-based simple file server to serve files of the current working directory

fileserve Golang-based simple file server to serve static files of the current working directory File sharing in LAN or home network Web application t

Jan 9, 2022
A go1.18 wrapper to provide simple generics based API for defining command line flags.

gflag A go1.18 wrapper to provide simple generics based API for defining command line flags. Example package main import ( "flag" "fmt" "time" "

Dec 20, 2021
A simple CLI based rock-paper-scissors game created in GO with interactive shell prompt.

rock-paper-scissors A simple CLI (Command Line Interface) based rock-paper-scissors game with interactive shell prompt. Language Download Grab a binar

Oct 9, 2022
📝 connote is a simple console-based note taking tool.

?? connote connote is a simple console-based note taking tool. Install Download the binary for your operating system from Releases Run go install gith

Jun 10, 2022
Simple to do list API with Gin and Gorm (with Postgres)Simple to do list API with Gin and Gorm (with Postgres)

go-todo Simple to do list API with Gin and Gorm (with Postgres) Docker Clone this repository and run: docker-compose up You can then hit the followin

Aug 29, 2022
Automatically sets up command line flags based on struct fields and tags.
Automatically sets up command line flags based on struct fields and tags.

Commandeer Commandeer sets up command line flags based on struct fields and tags. Do you... like to develop Go apps as libraries with tiny main packag

Dec 1, 2022
Tag-based environment configuration for structs

env Tag-based environment configuration for structs. Installation $ go get -u github.com/codingconcepts/env Usage package main import ( "fmt" "log"

Dec 23, 2022
Struct-based argument parsing in Go
Struct-based argument parsing in Go

go-arg Struct-based argument parsing for Go Declare command line arguments for your program by defining a struct. var args struct { Foo string Bar b

Jan 8, 2023
A terminal based typing test.
A terminal based typing test.

What A terminal based typing test. Installation Linux sudo curl -L https://github.com/lemnos/tt/releases/download/v0.4.0/tt-linux -o /usr/local/bin/tt

Dec 28, 2022
Raspberry Pi terminal based activity monitor
Raspberry Pi terminal based activity monitor

pitop Raspberry Pi terminal based activity monitor Yes I know there are plenty of solutions already available, but I wanted to build my own terminal b

Dec 11, 2022
A modern and intuitive terminal-based text editor
A modern and intuitive terminal-based text editor

micro is a terminal-based text editor that aims to be easy to use and intuitive, while also taking advantage of the capabilities of modern terminals

Jan 7, 2023
A CLI tool that generates OpenTelemetry Collector binaries based on a manifest.

OpenTelemetry Collector builder This program generates a custom OpenTelemetry Collector binary based on a given configuration. TL;DR $ go get github.c

Sep 14, 2022