{"id":16853574,"url":"https://github.com/foobarwidget/matchhostfsowner","last_synced_at":"2025-03-22T06:30:57.680Z","repository":{"id":44677738,"uuid":"191420401","full_name":"FooBarWidget/matchhostfsowner","owner":"FooBarWidget","description":"Solves the Docker host filesystem owner matching problem","archived":false,"fork":false,"pushed_at":"2023-07-21T08:00:43.000Z","size":1737,"stargazers_count":54,"open_issues_count":5,"forks_count":0,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-03-18T08:53:28.094Z","etag":null,"topics":["cloudnative","containerization","docker","kubernetes","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FooBarWidget.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY-NOTES.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-11T17:39:23.000Z","updated_at":"2024-08-05T04:11:06.000Z","dependencies_parsed_at":"2024-10-28T12:27:22.982Z","dependency_job_id":"4c3d78c7-5182-44ae-8b1f-bc7fb962da75","html_url":"https://github.com/FooBarWidget/matchhostfsowner","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FooBarWidget%2Fmatchhostfsowner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FooBarWidget%2Fmatchhostfsowner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FooBarWidget%2Fmatchhostfsowner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FooBarWidget%2Fmatchhostfsowner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FooBarWidget","download_url":"https://codeload.github.com/FooBarWidget/matchhostfsowner/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244918500,"owners_count":20531682,"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":["cloudnative","containerization","docker","kubernetes","rust"],"created_at":"2024-10-13T13:52:07.695Z","updated_at":"2025-03-22T06:30:57.144Z","avatar_url":"https://github.com/FooBarWidget.png","language":"Rust","readme":"# MatchHostFsOwner: solving the Docker host filesystem owner matching problem\n\nMatchHostFsOwner solves [the Docker host filesystem owner matching problem](https://www.joyfulbikeshedding.com/blog/2021-03-15-docker-and-the-host-filesystem-owner-matching-problem.html). It ensures that the container's UID/GID matches the host's. This way:\n\n * Containers avoid \"permission denied\" errors when accessing host files.\n * You can avoid running containers as root in an attempt to bypass permission problems.\n    - Containers running as root create root-owned files on the host, which may be unreadable by the host's user. This gives extra headaches!\n    - Running as root is insecure. Avoid it when possible!\n\n![Mascot](mascot-small.jpg)\n\n**Table of contents**\n\n - [What is the Docker host filesystem owner matching problem?](#what-is-the-docker-host-filesystem-owner-matching-problem)\n - [How does MatchHostFsOwner solve the problem?](#how-does-matchhostfsowner-solve-the-problem)\n - [Basic usage](#basic-usage)\n    - [Usage mode 1: start container without root privileges](#usage-mode-1-start-container-without-root-privileges)\n      - [Container image build instructions](#container-image-build-instructions)\n      - [Container start instructions](#container-start-instructions)\n        - [Docker CLI](#docker-cli)\n        - [Kubernetes](#kubernetes)\n    - [Usage mode 2: start container with root privileges](#usage-mode-2-start-container-with-root-privileges)\n      - [Container image build instructions](#container-image-build-instructions-1)\n      - [Container start instructions](#container-start-instructions-1)\n        - [Docker CLI](#docker-cli-1)\n        - [Docker Compose](#docker-compose)\n        - [Kubernetes](#kubernetes-1)\n - [Advanced usage](#advanced-usage)\n    - [Custom user/group account name](#custom-usergroup-account-name)\n    - [Combining other entrypoint programs with MatchHostFsOwner](#combining-other-entrypoint-programs-with-matchhostfsowner)\n      - [Wrapping MatchHostFsOwner around other entrypoint programs](#wrapping-matchhostfsowner-around-other-entrypoint-programs)\n      - [Wrapping other entrypoint programs around MatchHostFsOwner](#wrapping-other-entrypoint-programs-around-matchhostfsowner)\n    - [Hooks](#hooks)\n      - [Don't hardcode app account name or home directory!](#dont-hardcode-app-account-name-or-home-directory)\n - [Special considerations](#special-considerations)\n    - [Security of setuid root bit](#security-of-setuid-root-bit)\n    - [Chowning of home directory](#chowning-of-home-directory)\n      - [Disabling chowning](#disabling-chowning)\n      - [Chowning only selected files](#chowning-only-selected-files)\n - [Troubleshooting](#troubleshooting)\n - [Development](#development)\n\n## What is the Docker host filesystem owner matching problem?\n\n\u003e This is a quick summary. See [the article](https://www.joyfulbikeshedding.com/blog/2021-03-15-docker-and-the-host-filesystem-owner-matching-problem.html) for a full explanation.\n\nWhen a container accesses files on the host via host path mounts, one may run into permission problems.\n\n * If the container runs as a non-root user, one may encounter \"permission denied\" errors.\n * If the container runs as root, this is a security risk. Furthermore, the host directory may end up with a bunch of root-owned files, which may be unreadable by the host user.\n\nThese issues happen because apps in the container run as a different user than the host user. These issues plague any container that interacts with host files. For example, development environment containers tend to read source files on the host and write to log files on the host.\n\nThere are various strategies to solve this problem, but they are all either non-trivial (requiring complex logic) and/or have significant caveats (e.g. requiring privileged containers). See the article to learn more.\n\n## How does MatchHostFsOwner solve the problem?\n\nMatchHostFsOwner implements solution strategy number 1 [described in the article](https://www.joyfulbikeshedding.com/blog/2021-03-15-docker-and-the-host-filesystem-owner-matching-problem.html). It ensures that the container runs as the same user (UID/GID) as the host's user. In short, it:\n\n 1. ...modifies a user account inside the container so that the account's UID/GID matches that of the host user.\n 2. ...executes the actual container command as the aforementioned user account (instead of, e.g., letting it execute as root).\n\nThis strategy is easier said than done, and the article documents the many caveats involved with this strategy. Fortunately, MatchHostFsOwner is here to help because it addresses all these caveats, so you don't have to.\n\n## Basic usage\n\nCore concepts to understand:\n\n - **It's an entrypoint** — Install MatchHostFsOwner as the container entrypoint program. It [should be the first program to run in the container](#combining-other-entrypoint-programs-with-matchhostfsowner). When it runs, modifies the container's environment, then executes the next command with the proper UID/GID.\n\n - **It requires host user input** — when starting a container, the host user must tell MatchHostFsOwner what the host user's UID/GID is. How the user passes this information depends on what tool the user uses to start the container (e.g., Docker CLI, Docker Compose, Kubernetes, etc).\n\n - **It requires an extra user account in the container** — MatchHostFsOwner tries to execute the next command under a user account in the container whose UID equals the host user's UID. If no such account exists (which is common), then MatchHostFsOwner will take a specific account and modify its UID/GID to match that of the host user.\n\n   The account MatchHostFsOwner will take and modify is called the **\"app account\"**. MatchHostFsOwner won't create this account for you — you have to supply it. It won't always be used, but often it will.\n\n   By default, MatchHostFsOwner assumes that the app account is named `app`. But this is [customizable](#custom-usergroup-account-name).\n\n - **It requires root privileges** — MatchHostFsOwner itself requires root privileges to modify the container's environment. It drops these privileges later before executing the next command.\n\n   How exactly MatchHostFsOwner is granted root privileges depends on how one is supposed to start the container. This brings us to the two _usage modes_.\n\n### Usage mode 1: start container without root privileges\n\nThis mode is most suitable for starting the container without root privileges. For example:\n\n - When your Dockerfile sets a default user account using `USER`.\n - When your container is supposed to be started with `docker run --user`.\n - When your Kubernetes spec makes use of securityContext's `runAsUser`/`runAsGroup`.\n\nIn this mode, you must grant MatchHostFsOwner the setuid root bit. MatchHostFsOwner drops its setuid root bit as soon as possible after it has done its work.\n\nLimitations of this mode:\n\n - The container cannot be started a second time (e.g., using `docker stop` and then `docker start`). Upon starting the container for the second time, MatchHostFsOwner no longer has the setuid root bit, so it won't be able to do its job. Thus, mode 1 is only useful for ephemeral containers.\n - Incompatible with Docker Compose because it may start the container a second time.\n - Requires that the container filesystem in which MatchHostFsOwner is located, to be writable. Because MatchHostFsOwner must be able to drop the setuid root bit. Thus, you cannot run the container in read-only mode (e.g., `docker run --read-only`).\n\n#### Container image build instructions\n\n 1. Create an account named `app` in your container. Set it up as the default account for the container. [A different account name is also possible](#custom-usergroup-account-name).\n 2. Place the MatchHostFsOwner executable in a root-owned directory (e.g., `/sbin`) and ensure that the executable is owned by root, and has the setuid root bit.\n 3. Set up the MatchHostFsOwner executable as the container entrypoint.\n\nExample:\n\n~~~dockerfile\nFROM ubuntu:22.04\n\n# Install MatchHostFsOwner. Replace X.X.X with an actual version.\n# See https://github.com/FooBarWidget/matchhostfsowner/releases\nADD https://github.com/FooBarWidget/matchhostfsowner/releases/download/vX.X.X/matchhostfsowner-X.X.X-x86_64-linux.gz /sbin/matchhostfsowner.gz\nRUN gunzip /sbin/matchhostfsowner.gz \u0026\u0026 \\\n  chown root: /sbin/matchhostfsowner \u0026\u0026 \\\n  chmod +x,+s /sbin/matchhostfsowner\nRUN addgroup --gid 9999 app \u0026\u0026 \\\n  adduser --uid 9999 --gid 9999 --disabled-password --gecos App app\n## Or, on RHEL-based images:\n# RUN groupadd --gid 9999 app \u0026\u0026 \\\n#   useradd --uid 9999 --gid 9999 app\n## Or, on Alpine-based images:\n# RUN addgroup -g 9999 app \u0026\u0026 \\\n#   adduser -G app -u 9999 -D app\nUSER app\n\nENTRYPOINT [\"/sbin/matchhostfsowner\"]\n~~~\n\n~~~bash\ndocker build . -t my-example-image\n~~~\n\n#### Container start instructions\n\n##### Docker CLI\n\nSet `--user` to the host user's UID and GID. MatchHostFsOwner will recognize that UID/GID as being the host user's. Example:\n\n~~~bash\ndocker run --user \"$(id -u):$(id -g)\" my-example-image id -a\n# Output (assuming host UID/GID is 501/20):\n# uid=501(app) gid=20(app) groups=20(app)\n~~~\n\n##### Kubernetes\n\nSet `spec.securityContext.runAsUser` and `spec.securityContext.runAsGroup` to appropriate values. MatchHostFsOwner will recognize that UID/GID as being the host user's. Example:\n\n~~~yaml\napiVersion: v1\nkind: Pod\nmetadata:\n  name: matchhostfsowner-demo\nspec:\n  securityContext:\n    runAsUser: \u003cHOST UID HERE\u003e\n    runAsGroup: \u003cHOST GID HERE\u003e\n  volumes:\n    - name: host\n      hostPath:\n        path: /some-path-on-the-host\n        type: Directory\n  containers:\n    - name: matchhostfsowner-demo\n      image: busybox\n      command: [\"touch\", \"/host/foo.txt\"]\n      volumeMounts:\n        - name: host\n          mountPath: /host\n~~~\n\n### Usage mode 2: start container with root privileges\n\nIn this mode, MatchHostFsOwner obtains root privileges through the fact that one starts the container with root privileges. MatchHostFsOwner drops its root privileges as soon as possible after it has done its work.\n\nThis mode is most suitable if any of the following is applicable:\n\n - You're using Docker Compose.\n - The container could be started a second time, as happens with, e.g., Docker Compose.\n - The container filesystem in which MatchHostFsOwner is located is read-only.\n\n#### Container image build instructions\n\n 1. Create an account named `app` in your container. [A different account name is also possible](#custom-usergroup-account-name).\n 2. Place the MatchHostFsOwner executable in a root-owned directory (e.g., `/sbin`) and ensure that the executable is owned by root.\n 3. Set up the MatchHostFsOwner executable as the container entrypoint.\n 4. **Don't** set a default user account with `USER`.\n\nExample:\n\n~~~dockerfile\nFROM ubuntu:22.04\n\n# Install MatchHostFsOwner. Replace X.X.X with an actual version.\n# See https://github.com/FooBarWidget/matchhostfsowner/releases\nADD https://github.com/FooBarWidget/matchhostfsowner/releases/download/vX.X.X/matchhostfsowner-X.X.X-x86_64-linux.gz /sbin/matchhostfsowner.gz\nRUN gunzip /sbin/matchhostfsowner.gz \u0026\u0026 \\\n  chown root: /sbin/matchhostfsowner \u0026\u0026 \\\n  chmod +x /sbin/matchhostfsowner\nRUN addgroup --gid 9999 app \u0026\u0026 \\\n  adduser --uid 9999 --gid 9999 --disabled-password --gecos App app\n## Or, on RHEL-based images:\n# RUN groupadd --gid 9999 app \u0026\u0026 \\\n#   useradd --uid 9999 --gid 9999 app\n## Or, on Alpine-based images:\n# RUN addgroup -g 9999 app \u0026\u0026 \\\n#   adduser -G app -u 9999 -D app\n\nENTRYPOINT [\"/sbin/matchhostfsowner\"]\n~~~\n\n~~~bash\ndocker build . -t my-example-image\n~~~\n\n#### Container start instructions\n\n##### Docker CLI\n\nSet the environment variables `MHF_HOST_UID` and `MHF_HOST_GID` to the host user's UID/GID. Example:\n\n~~~bash\ndocker run -e \"MHF_HOST_UID=$(id -u)\" -e \"MHF_HOST_GID=$(id -g)\" my-example-image id -a\n# Output (assuming host UID/GID is 501/20):\n# uid=501(app) gid=20(app) groups=20(app)\n~~~\n\n##### Docker Compose\n\nIn your `docker-compose.yml`, ensure you pass the `UID` and `GID` CLI environment variables, into the corresponding containers' environment variables as `MHF_HOST_UID` and `MHF_HOST_GID`:\n\n~~~yaml\nservices:\n  foo:\n    ...\n    environment:\n      - MHF_HOST_UID=${UID}\n      - MHF_HOST_GID=${GID}\n~~~\n\nThen every time the user runs `docker-compose up`, the user must pass the `UID` and `GID` environment variables. These are to be set to the user's own UID/GID:\n\n~~~bash\nexport UID  # The shell already sets $UID as a read-only variable but does not export it\nexport GID=\"$(id -g)\"\ndocker-compose up\n~~~\n\n\u003e **Tip**: Remembering to set \\$UID/\\$GID every time can be a bit inconvenient. The user can use [direnv](https://direnv.net/) to automate this. For example, in the project's `.envrc`:\n\u003e\n\u003e ~~~bash\n\u003e export UID  # The shell already sets $UID as a read-only variable but does not export it\n\u003e export GID=\"$(id -g)\"\n\u003e ~~~\n\n##### Kubernetes\n\nSet the container environment variables `MHF_HOST_UID` and `MHF_HOST_GID` to appropriate values. Example:\n\n~~~yaml\napiVersion: v1\nkind: Pod\nmetadata:\n  name: security-context-demo\nspec:\n  securityContext:\n    runAsUser: \u003cHOST UID HERE\u003e\n    runAsGroup: \u003cHOST GID HERE\u003e\n  volumes:\n    - name: host\n      hostPath:\n        path: /some-path-on-the-host\n        type: Directory\n  containers:\n    - name: matchhostfsowner-demo\n      image: busybox\n      command: [\"touch\", \"/host/foo.txt\"]\n      env:\n        - name: MHF_HOST_UID\n          value: \"\u003cHOST UID HERE\u003e\"\n        - name: MHF_HOST_GID\n          value: \"\u003cHOST GID HERE\u003e\"\n      volumeMounts:\n        - name: host\n          mountPath: /host\n~~~\n\n## Advanced usage\n\n### Custom user/group account name\n\nIf you want MatchHostFsOwner to use a different user account or group account in the container, then you can customize this with the `app_account` and `app_group` config options.\n\nInside the container, create a file /etc/matchhostfsowner/config.yml:\n\n~~~yaml\napp_account: \u003cUSER ACCOUNT NAME HERE\u003e\napp_group: \u003cGROUP ACCOUNT NAME HERE\u003e\n~~~\n\nMake sure to protect this file and directory appropriately:\n\n~~~bash\nchown -R root: /etc/matchhostfsowner \u0026\u0026 \\\nchmod 700 /etc/matchhostfsowner \u0026\u0026 \\\nchmod 600 /etc/matchhostfsowner/*\n~~~\n\n### Combining other entrypoint programs with MatchHostFsOwner\n\nYou can combine other entrypoint programs with MatchHostFsOwner in three ways:\n\n * Wrapping MatchHostFsOwner around other entrypoint programs.\n    - This runs other entrypoint programs under a normal user account.\n    - By the time other entrypoint programs run, MatchHostFsOwner has already done its job, so it's fine for other entrypoint programs to access host files.\n * Wrapping MatchHostFsOwner around other entrypoint programs.\n    - Under usage mode 1, this runs other entrypoint programs under a normal user account.\n    - Under usage mode 2, this runs other entrypoint programs as root.\n    - By the time other entrypoint programs run, MatchHostFsOwner has *not* done its job. So be careful with accessing host files in those entrypoint programs, especially because the effective user is different for each usage mode.\n * By using [hooks](#hooks) (**recommended**).\n    - Hooks are always run as root, regardless of usage mode.\n    - Hooks are run after MatchHostFsOwner has done most of its work, but before MatchHostFsOwner has dropped its privileges.\n\n#### Wrapping MatchHostFsOwner around other entrypoint programs\n\nMatchHostFsOwner works by executing the command passed through its arguments after MatchHostFsOwner has done its work. Thus, you can wrap other entrypoint programs by passing them (and their arguments) to MatchHostFsOwner as arguments.\n\nFor example let's say that your container currently has an entrypoint program that prints \"hello world\", and that the container's main command touches a host-mounted file, like this:\n\n~~~dockerfile\nFROM ubuntu:22.04\n\nADD my_entrypoint.sh /\n\nENTRYPOINT [\"/my_entrypoint.sh\"]\nCMD [\"touch\", \"/host/foo.txt\"]\n~~~\n\nmy_entrypoint.sh looks like this:\n\n~~~bash\n#!/usr/bin/env sh\necho \"Hello world from entrypoint\"\nexec \"$@\"\n~~~\n\nYou can combine it with MatchHostFsOwner like this:\n\n~~~dockerfile\nFROM ubuntu:22.04\n\n# Install MatchHostFsOwner. Replace X.X.X with an actual version.\n# See https://github.com/FooBarWidget/matchhostfsowner/releases\nADD https://github.com/FooBarWidget/matchhostfsowner/releases/download/vX.X.X/matchhostfsowner-X.X.X-x86_64-linux.gz /sbin/matchhostfsowner.gz\nRUN gunzip /sbin/matchhostfsowner.gz \u0026\u0026 \\\n  chown root: /sbin/matchhostfsowner \u0026\u0026 \\\n  chmod +x,+s /sbin/matchhostfsowner\nRUN addgroup --gid 9999 app \u0026\u0026 \\\n  adduser --uid 9999 --gid 9999 --disabled-password --gecos App app\nUSER app\n\nADD my_entrypoint.sh /\n\n# Wrap MatchHostFsOwner around my_entrypoint.sh\nENTRYPOINT [\"/sbin/matchhostfsowner\", \"/my_entrypoint.sh\"]\nCMD [\"touch\", \"/host/foo.txt\"]\n~~~\n\n#### Wrapping other entrypoint programs around MatchHostFsOwner\n\nBe aware when doing this. Your other entrypoint programs shouldn't access host files, because at that point MatchHostFsOwner hasn't done its job yet. If that's not an issue, then read on.\n\nLet's say that your container currently has an entrypoint program that prints \"hello world\", and that the container's main command touches a host-mounted file, like this:\n\n~~~dockerfile\nFROM ubuntu:22.04\n\nADD my_entrypoint.sh /\n\nENTRYPOINT [\"/my_entrypoint.sh\"]\nCMD [\"touch\", \"/host/foo.txt\"]\n~~~\n\nmy_entrypoint.sh looks like this:\n\n~~~bash\n#!/usr/bin/env sh\necho \"Hello world from entrypoint\"\nexec \"$@\"\n~~~\n\nYou can combine it with MatchHostFsOwner like this:\n\n~~~dockerfile\nFROM ubuntu:22.04\n\n# Install MatchHostFsOwner. Replace X.X.X with an actual version.\n# See https://github.com/FooBarWidget/matchhostfsowner/releases\nADD https://github.com/FooBarWidget/matchhostfsowner/releases/download/vX.X.X/matchhostfsowner-X.X.X-x86_64-linux.gz /sbin/matchhostfsowner.gz\nRUN gunzip /sbin/matchhostfsowner.gz \u0026\u0026 \\\n  chown root: /sbin/matchhostfsowner \u0026\u0026 \\\n  chmod +x,+s /sbin/matchhostfsowner\nRUN addgroup --gid 9999 app \u0026\u0026 \\\n  adduser --uid 9999 --gid 9999 --disabled-password --gecos App app\nUSER app\n\nADD my_entrypoint.sh /\n\n# Wrap my_entrypoint.sh around MatchHostFsOwner\nENTRYPOINT [\"/my_entrypoint.sh\", \"/sbin/matchhostfsowner\"]\nCMD [\"touch\", \"/host/foo.txt\"]\n~~~\n\n### Hooks\n\nAfter MatchHostFsOwner has done most of its work, but before it has dropped privileges and executed the next command, MatchHostFsOwner will run *hooks*. Hooks are useful if you want to perform additional setup at this point. For example, if your container already had an entrypoint script, then should convert that into a hook.\n\nA hook is any executable file in the container inside `/etc/matchhostfsowner/hooks.d`. Hooks are always run as root. Hooks are run serially, in alphabetical order. Hooks must all succeed: if any hook exits with a non-successful code, then MatchHostFsOwner aborts with an error.\n\nThe following environment variables are passed to hooks:\n\n * `MHF_HOST_UID` — the host user's UID, i.e., the UID of the container user account that we will use.\n * `MHF_HOST_GID` — the host user's GID, i.e., the GID of the container group account that we will use.\n * `MHF_HOST_USER` — the name of the container user account that we will use.\n * `MHF_HOST_GROUP` — the name of the container group account that we will use.\n * `MHF_HOST_HOME` — the home directory of the container user account that we will use.\n\n#### Hooks example\n\nLet's say that your container currently has an entrypoint program that prints \"hello world\", and that the container's main command touches a host-mounted file, like this:\n\n~~~dockerfile\nFROM ubuntu:22.04\n\nADD my_entrypoint.sh /\n\nENTRYPOINT [\"/my_entrypoint.sh\"]\nCMD [\"touch\", \"/host/foo.txt\"]\n~~~\n\nmy_entrypoint.sh looks like this:\n\n~~~bash\n#!/usr/bin/env sh\necho \"Hello world from entrypoint\"\nexec \"$@\"\n~~~\n\nLet's convert this example into one that uses hooks:\n\n~~~dockerfile\nFROM ubuntu:22.04\n\n# Install MatchHostFsOwner. Replace X.X.X with an actual version.\n# See https://github.com/FooBarWidget/matchhostfsowner/releases\nADD https://github.com/FooBarWidget/matchhostfsowner/releases/download/vX.X.X/matchhostfsowner-X.X.X-x86_64-linux.gz /sbin/matchhostfsowner.gz\nRUN gunzip /sbin/matchhostfsowner.gz \u0026\u0026 \\\n  chown root: /sbin/matchhostfsowner \u0026\u0026 \\\n  chmod +x,+s /sbin/matchhostfsowner\nRUN addgroup --gid 9999 app \u0026\u0026 \\\n  adduser --uid 9999 --gid 9999 --disabled-password --gecos App app\nUSER app\n\n# Install my_entrypoint.sh as a hook\nADD my_entrypoint.sh /etc/matchhostfsowner/hooks.d/\n\n# Replace entrypoint with MatchHostFsOwner\nENTRYPOINT [\"/sbin/matchhostfsowner\"]\nCMD [\"touch\", \"/host/foo.txt\"]\n~~~\n\nmy_entrypoint.sh also needs some changes. That script executes the next command, but MatchHostFsOwner will run it without any arguments. So we change it to this:\n\n~~~bash\n#!/usr/bin/env sh\necho \"Hello world from hook\"\n~~~\n\n### Don't hardcode app account name or home directory!\n\nDon't hardcode the app account's name or home directory inside hooks. Use the above environment variables instead. MatchHostFsOwner isn't guaranteed to use the app account even if it usually will.\n\nDon't do this:\n\n~~~bash\n#!/usr/bin/env sh\nset -e\nchown app: /some/file\ntouch /home/app/foo\n~~~\n\nDo this instead:\n\n~~~bash\n#!/usr/bin/env sh\nset -e\nchown \"$MHF_HOST_USER\": /some/file\ntouch \"$MHF_HOST_HOME/foo\"\n~~~\n\n## Special considerations\n\n### Security of setuid root bit\n\n\u003e See also the [Security notes](SECURITY-NOTES.md)\n\nIn usage mode 1, MatchHostFsOwner requires the setuid root bit. How secure is this?\n\nOne attack vector that we foresee is that a program in the container tries to run MatchHostFsOwner with malicious configuration, tricking MatchHostFsOwner into performing malicious activity while having root privileges. We take the following security precautions:\n\n * We drop the setuid root bit from the MatchHostFsOwner executable file as soon as possible, so that after MatchHostFsOwner has done its work it cannot gain root privileges again. This is why usage mode 1 is not compatible with a read-only filesystem.\n * We reset `PATH` into a well-known value, instead of using the PATH that was given to us. This way we prevent MatchHostFsOwner from executing malicious versions of otherwise innocent commands (e.g. `find`).\n * We require one of the following conditions to be true:\n\n     - MatchHostFsOwner is PID 1.\n     - MatchHostFsOwner is a child of PID 1, and PID 1 is the Docker init process (as spawned by `docker run --init`).\n\n   This way we attempt to limit MatchHostFsOwner into only being able to run during container startup.\n\n### Chowning of home directory\n\nWhen MatchHostFsOwner modifies the UID/GID of a container user/group account, one special problem occurs: the user's home directory is still owned by the old UID/GID. This home directory may become unwritable by that user account, or even unreadable. This could cause many problems because most apps expect the user's home directory to be readable and writable.\n\nTo avoid this problem, MatchHostFsOwner changes (`chown`s) the UID/GID of the home directory. This happens recursively for all its contents but does not cross filesystem boundaries (so it won't chown for example volume mounts in the home directory).\n\nHowever, if the home directory contains many files, then chowning may take a long. In this case, you may want to consider one of these two different approaches instead:\n\n * Disable chowning entirely.\n * Chown only selected files, e.g. only the home directory itself, plus the files/directories immediately under the home directory, but not recursively. This may sometimes be acceptable because files/directories tend to be world-readable.\n\n#### Disabling chowning\n\nTo disable chowning, create a file /etc/matchhostfsowner/config.yml inside the container:\n\n~~~yaml\nchown_home: false\n~~~\n\nMake sure to protect this file and directory appropriately:\n\n~~~bash\nchown -R root: /etc/matchhostfsowner \u0026\u0026 \\\nchmod 700 /etc/matchhostfsowner \u0026\u0026 \\\nchmod 600 /etc/matchhostfsowner/*\n~~~\n\n#### Chowning only selected files\n\nFirst, disable chowning as described in the previous section. Then create a [hook](#hooks) that chowns whatever you want. For example, create /etc/matchhostfsowner/hooks.d/chown.sh inside the container:\n\n~~~bash\n#!/usr/bin/env bash\nset -e\n\n# Chown the home directory itself\nchown \"$MHF_HOST_USER\": \"$MHF_HOST_HOME\"\n\n# Chown files/directories immediately below the home directory, non-recursively\nshopt -u dotglob\nchown \"$MHF_HOST_USER\": -- \"$MHF_HOST_HOME\"/*\n~~~\n\nDon't forget to ensure that /etc/matchhostfsowner/hooks.d/chown.sh is owned by root and executable.\n\n## Troubleshooting\n\nIf something goes wrong and you don't know why then set the environment variable `MHF_LOG_LEVEL=debug` to see debugging logs.\n\n## Development\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md).\n\nThe mascot is generated by DALL-E.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffoobarwidget%2Fmatchhostfsowner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffoobarwidget%2Fmatchhostfsowner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffoobarwidget%2Fmatchhostfsowner/lists"}