Skip to content

theAkito/userdef

Repository files navigation

nimble

Source Language

Last Commit

GitHub Liberapay patrons

What

This tool is a more advanced adduser / useradd for your Alpine and BusyBox based Docker images.

For example, this tool may create a custom OS user with a custom ID inside pre-made Docker images, which perhaps already have a custom user defined and rebuilding the Docker image just to have your custom user in it is not an option.

Why

Reason 1

Now, more and more server apps try to go with the current meta of being available on Kubernetes, etc. This is a good idea, however it's often not well executed. Almost all of the popular server apps are not cloud-native. Their structure is still of some legacy kind. Examples are Mattermost, Gitea, Nextcloud. These server apps have Helm Charts available. However, applying best practices, especially the ones regarding security are not easy to achieve. Especially, when talking about the podSecurityContext. The fsGroup option is either not respected properly, which leads to broken deployments, or it's not even available and you have to add it yourself to the Helm Chart.

For example, take the Gitea Helm Chart as an example. You are allowed to set the podSecurityContext: https://gitea.com/gitea/helm-chart/src/commit/d94226765d6e1f197a3112e1b1abbcd73a8bea33/values.yaml#L19-L20

But, if you provide your custom fsGroup value, then the deployment will be broken. Why? https://github.com/go-gitea/gitea/blob/66f2210feca0b50d305a46a203c2b3d2f4d3790b/Dockerfile.rootless#L39-L48

Because the user and group ID of 1000 is hard-coded into the Docker image.

Now, imagine you have an sshfs mount, which requires you using the user of the ID 9234. The hard-coded 1000 inside the image breaks usage of this sshfs mount, just because it does not let you define a custom user with a custom ID.

To make all this work more smoothly, this tool aims to delete the existing user in that Docker image and then recreate it with your custom user, which has an ID defined by you, instead of being forced to use the randomly chosen hard-coded user ID.

Reason 2

You may just as well use this tool as a better adduser where the actual adduser or useradd (like the one in Alpine) have arbitrary and unnecessary restrictions, like for example limiting the UID/GID size to 256000.

How

Example using the rootless Docker image for Gitea:

## Get the binary.
## The default Docker Tag provides the Alpine (musl) based binary.
FROM akito13/userdef AS base
## Pull the image you want to modify the executing user of.
FROM gitea/gitea:1.16.5-linux-amd64-rootless

## We temporarily need to use the root user,
## as we are doing administrative tasks, like e.g. modifying an OS user.
USER root:root
COPY --from=base /userdef /userdef
## 1. Change the existing user.
## 2. Use that user to `chown` relevant folders.
## 3. Remove the binary, because the user has been changed,
##    i.e. our job is done here.
RUN /userdef -h=/var/lib/gitea/git -n=git -u=9234 -g=9234 && \
  chown git:git -R /var/lib/gitea /etc/gitea && \
  rm -f /userdef

## Switch to the now relevant executing user.
USER 9234:9234
## Taken from https://github.com/go-gitea/gitea/blob/66f2210feca0b50d305a46a203c2b3d2f4d3790b/Dockerfile.rootless#L71-L72
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
CMD []

This way, you get the behaviour of the original Docker image, but instead being forced to use the hard-coded user ID, you adjust the user inside the image to the one you specify.

For a live example, run nimble example.

CLI Usage

Usage:
  userdef -n=<user-name> -u=<user-id> -h=<user-home>
          [-g=<user-group-id>] [-l | -l=[true|false]]
          [-c=<path-to-config-file> | <path-to-config-file>]

Examples:
  userdef --help
  userdef -h=/var/lib/gitea/git -n=git -u=9234 -g=9234
  userdef -h=/home/langlang -n=langlang -u=290111 -g=290111 --long
  userdef -h=/overwrites/home/value/in/userdef.json -l=true /path/to/userdef.json

Options:
  -n, --name            Name of the user to modify or add.
  -u, --uid             User ID.
  -h, --home            Path to user's home.
  -c, --config          (Optional) Provide path to configuration file.
  -g, --gid             (Optional) Group ID. If empty, then GID will be same as UID.
  -l, --long            (Optional) Whether long IDs (greater than 256000) are guaranteed to be supported.
  -v, --version         App version information.
  --help                This help text.

Hints:
  * If a user with the provided name already exists,
    then it will be deleted and a new one will be created,
    to replace the original one.

  * Providing a configuration file works by using the `--config` option or
    by providing the path without using any option.

  * You may replace the equal signs with colons when providing CLI arguments.
    Example: userdef -h:/home/langlang -n:langlang -u:290111 -g:290111 --long

  * You also may replace the equal signs with nothing when providing CLI arguments.
    Example: userdef -h/home/langlang -nlanglang -u290111 -g290111 --long

Alternative Sources

Last week i bought a chain saw with a twisted handle. Perhaps i wasn't careful, but by accident it chopped one of my arm off, then i thought to myself "gosh, this is POWERFUL!". [...]

---- Xah Lee


If you are one of those enthusiastic Linux fans, who want to do anything possible on Linux, even if it does not make sense, do not worry, I have you covered.

Installing the binary using Nim's package manager:

nimble install userdef

Installing the binary using Github Releases:

curl -fsSLo userdef https://github.com/theAkito/userdef/releases/download/0.3.0/userdef-github-0.3.0-linux-amd64
chmod +x userdef
mv userdef /usr/bin/userdef

Where

Docker containers running Docker images based on Linux. You will need it most likely on BusyBox based images, like Alpine.

Only Linux is officially supported.

Works on libc (normal Linux) and musl (Alpine, Busybox, etc.) based Docker images.

For further information on this topic, please visit this project's Docker Hub page.

Goals

  • Reliability

Project Status

Stable Beta.

This app is well tested & works, but needs more testing and feedback from 3rd parties. --> Please help!

TODO

  • Make ID adjustable
  • Make Name adjustable
  • Read from config.json
  • Support long and short IDs
  • Add base Dockerfile
  • Add support for multi-arch Docker image
  • Add some kind of Continuous Delivery for binary in Docker image
  • Add meaningful example in README
  • Add libc based Docker images for binary provision (Alpine is musl based)
  • Add CLI Usage Info to README
  • Publish to Nimble
  • Publish to Awesome Docker
  • Use Nimscript instead of Bash for Build scripts
  • Add nim.cfg for optimised nimble install build
  • Test with GID different from UID
  • Provide BUILD_VERSION, BUILD_REVISION, BUILD_DATE in Docker Release images
  • Add Github Release
  • Use nim-useradd library for the backend
  • Add meaningful practical examples
  • Parse root Dockerfile and extract correct original user ID and user name

License

Copyright © 2022-2023 Akito the@akito.ooo

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.