diff options
| author | Craig Jennings <c@cjennings.net> | 2024-04-07 13:41:34 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2024-04-07 13:41:34 -0500 |
| commit | 754bbf7a25a8dda49b5d08ef0d0443bbf5af0e36 (patch) | |
| tree | f1190704f78f04a2b0b4c977d20fe96a828377f1 /devdocs/docker/engine%2Fsecurity%2Fuserns-remap%2Findex.html | |
new repository
Diffstat (limited to 'devdocs/docker/engine%2Fsecurity%2Fuserns-remap%2Findex.html')
| -rw-r--r-- | devdocs/docker/engine%2Fsecurity%2Fuserns-remap%2Findex.html | 46 |
1 files changed, 46 insertions, 0 deletions
diff --git a/devdocs/docker/engine%2Fsecurity%2Fuserns-remap%2Findex.html b/devdocs/docker/engine%2Fsecurity%2Fuserns-remap%2Findex.html new file mode 100644 index 00000000..e9b3ea56 --- /dev/null +++ b/devdocs/docker/engine%2Fsecurity%2Fuserns-remap%2Findex.html @@ -0,0 +1,46 @@ +<h1>Isolate containers with a user namespace</h1> + +<p>Linux namespaces provide isolation for running processes, limiting their access to system resources without the running process being aware of the limitations. For more information on Linux namespaces, see <a href="https://www.linux.com/news/understanding-and-securing-linux-namespaces" target="_blank" rel="noopener" class="_">Linux namespaces</a>.</p> <p>The best way to prevent privilege-escalation attacks from within a container is to configure your container’s applications to run as unprivileged users. For containers whose processes must run as the <code class="language-plaintext highlighter-rouge">root</code> user within the container, you can re-map this user to a less-privileged user on the Docker host. The mapped user is assigned a range of UIDs which function within the namespace as normal UIDs from 0 to 65536, but have no privileges on the host machine itself.</p> <h2 id="about-remapping-and-subordinate-user-and-group-ids">About remapping and subordinate user and group IDs</h2> <p>The remapping itself is handled by two files: <code class="language-plaintext highlighter-rouge">/etc/subuid</code> and <code class="language-plaintext highlighter-rouge">/etc/subgid</code>. Each file works the same, but one is concerned with the user ID range, and the other with the group ID range. Consider the following entry in <code class="language-plaintext highlighter-rouge">/etc/subuid</code>:</p> <pre data-language="">testuser:231072:65536 +</pre> <p>This means that <code class="language-plaintext highlighter-rouge">testuser</code> is assigned a subordinate user ID range of <code class="language-plaintext highlighter-rouge">231072</code> and the next 65536 integers in sequence. UID <code class="language-plaintext highlighter-rouge">231072</code> is mapped within the namespace (within the container, in this case) as UID <code class="language-plaintext highlighter-rouge">0</code> (<code class="language-plaintext highlighter-rouge">root</code>). UID <code class="language-plaintext highlighter-rouge">231073</code> is mapped as UID <code class="language-plaintext highlighter-rouge">1</code>, and so forth. If a process attempts to escalate privilege outside of the namespace, the process is running as an unprivileged high-number UID on the host, which does not even map to a real user. This means the process has no privileges on the host system at all.</p> <blockquote> <p>Multiple ranges</p> <p>It is possible to assign multiple subordinate ranges for a given user or group by adding multiple non-overlapping mappings for the same user or group in the <code class="language-plaintext highlighter-rouge">/etc/subuid</code> or <code class="language-plaintext highlighter-rouge">/etc/subgid</code> file. In this case, Docker uses only the first five mappings, in accordance with the kernel’s limitation of only five entries in <code class="language-plaintext highlighter-rouge">/proc/self/uid_map</code> and <code class="language-plaintext highlighter-rouge">/proc/self/gid_map</code>.</p> </blockquote> <p>When you configure Docker to use the <code class="language-plaintext highlighter-rouge">userns-remap</code> feature, you can optionally specify an existing user and/or group, or you can specify <code class="language-plaintext highlighter-rouge">default</code>. If you specify <code class="language-plaintext highlighter-rouge">default</code>, a user and group <code class="language-plaintext highlighter-rouge">dockremap</code> is created and used for this purpose.</p> <blockquote class="warning-vanila"> <p><strong>Warning</strong>: Some distributions, such as RHEL and CentOS 7.3, do not automatically add the new group to the <code class="language-plaintext highlighter-rouge">/etc/subuid</code> and <code class="language-plaintext highlighter-rouge">/etc/subgid</code> files. You are responsible for editing these files and assigning non-overlapping ranges, in this case. This step is covered in <a href="#prerequisites">Prerequisites</a>.</p> </blockquote> <p>It is very important that the ranges do not overlap, so that a process cannot gain access in a different namespace. On most Linux distributions, system utilities manage the ranges for you when you add or remove users.</p> <p>This re-mapping is transparent to the container, but introduces some configuration complexity in situations where the container needs access to resources on the Docker host, such as bind mounts into areas of the filesystem that the system user cannot write to. From a security standpoint, it is best to avoid these situations.</p> <h2 id="prerequisites">Prerequisites</h2> <ol> <li> <p>The subordinate UID and GID ranges must be associated with an existing user, even though the association is an implementation detail. The user owns the namespaced storage directories under <code class="language-plaintext highlighter-rouge">/var/lib/docker/</code>. If you don’t want to use an existing user, Docker can create one for you and use that. If you want to use an existing username or user ID, it must already exist. Typically, this means that the relevant entries need to be in <code class="language-plaintext highlighter-rouge">/etc/passwd</code> and <code class="language-plaintext highlighter-rouge">/etc/group</code>, but if you are using a different authentication back-end, this requirement may translate differently.</p> <p>To verify this, use the <code class="language-plaintext highlighter-rouge">id</code> command:</p> <div class="highlight"><pre class="highlight" data-language="">$ id testuser + +uid=1001(testuser) gid=1001(testuser) groups=1001(testuser) +</pre></div> </li> <li> <p>The way the namespace remapping is handled on the host is using two files, <code class="language-plaintext highlighter-rouge">/etc/subuid</code> and <code class="language-plaintext highlighter-rouge">/etc/subgid</code>. These files are typically managed automatically when you add or remove users or groups, but on a few distributions such as RHEL and CentOS 7.3, you may need to manage these files manually.</p> <p>Each file contains three fields: the username or ID of the user, followed by a beginning UID or GID (which is treated as UID or GID 0 within the namespace) and a maximum number of UIDs or GIDs available to the user. For instance, given the following entry:</p> <pre>testuser:231072:65536 +</pre> <p>This means that user-namespaced processes started by <code class="language-plaintext highlighter-rouge">testuser</code> are owned by host UID <code class="language-plaintext highlighter-rouge">231072</code> (which looks like UID <code class="language-plaintext highlighter-rouge">0</code> inside the namespace) through 296607 (231072 + 65536 - 1). These ranges should not overlap, to ensure that namespaced processes cannot access each other’s namespaces.</p> <p>After adding your user, check <code class="language-plaintext highlighter-rouge">/etc/subuid</code> and <code class="language-plaintext highlighter-rouge">/etc/subgid</code> to see if your user has an entry in each. If not, you need to add it, being careful to avoid overlap.</p> <p>If you want to use the <code class="language-plaintext highlighter-rouge">dockremap</code> user automatically created by Docker, check for the <code class="language-plaintext highlighter-rouge">dockremap</code> entry in these files <strong>after</strong> configuring and restarting Docker.</p> </li> <li> <p>If there are any locations on the Docker host where the unprivileged user needs to write, adjust the permissions of those locations accordingly. This is also true if you want to use the <code class="language-plaintext highlighter-rouge">dockremap</code> user automatically created by Docker, but you can’t modify the permissions until after configuring and restarting Docker.</p> </li> <li> <p>Enabling <code class="language-plaintext highlighter-rouge">userns-remap</code> effectively masks existing image and container layers, as well as other Docker objects within <code class="language-plaintext highlighter-rouge">/var/lib/docker/</code>. This is because Docker needs to adjust the ownership of these resources and actually stores them in a subdirectory within <code class="language-plaintext highlighter-rouge">/var/lib/docker/</code>. It is best to enable this feature on a new Docker installation rather than an existing one.</p> <p>Along the same lines, if you disable <code class="language-plaintext highlighter-rouge">userns-remap</code> you can’t access any of the resources created while it was enabled.</p> </li> <li> <p>Check the <a href="#user-namespace-known-limitations">limitations</a> on user namespaces to be sure your use case is possible.</p> </li> </ol> <h2 id="enable-userns-remap-on-the-daemon">Enable userns-remap on the daemon</h2> <p>You can start <code class="language-plaintext highlighter-rouge">dockerd</code> with the <code class="language-plaintext highlighter-rouge">--userns-remap</code> flag or follow this procedure to configure the daemon using the <code class="language-plaintext highlighter-rouge">daemon.json</code> configuration file. The <code class="language-plaintext highlighter-rouge">daemon.json</code> method is recommended. If you use the flag, use the following command as a model:</p> <div class="highlight"><pre class="highlight" data-language="">$ dockerd --userns-remap="testuser:testuser" +</pre></div> <ol> <li> <p>Edit <code class="language-plaintext highlighter-rouge">/etc/docker/daemon.json</code>. Assuming the file was previously empty, the following entry enables <code class="language-plaintext highlighter-rouge">userns-remap</code> using user and group called <code class="language-plaintext highlighter-rouge">testuser</code>. You can address the user and group by ID or name. You only need to specify the group name or ID if it is different from the user name or ID. If you provide both the user and group name or ID, separate them by a colon (<code class="language-plaintext highlighter-rouge">:</code>) character. The following formats all work for the value, assuming the UID and GID of <code class="language-plaintext highlighter-rouge">testuser</code> are <code class="language-plaintext highlighter-rouge">1001</code>:</p> <ul> <li><code class="language-plaintext highlighter-rouge">testuser</code></li> <li><code class="language-plaintext highlighter-rouge">testuser:testuser</code></li> <li><code class="language-plaintext highlighter-rouge">1001</code></li> <li><code class="language-plaintext highlighter-rouge">1001:1001</code></li> <li><code class="language-plaintext highlighter-rouge">testuser:1001</code></li> <li><code class="language-plaintext highlighter-rouge">1001:testuser</code></li> </ul> <div class="highlight"><pre class="highlight" data-language="">{ + "userns-remap": "testuser" +} +</pre></div> <blockquote> <p><strong>Note</strong>: To use the <code class="language-plaintext highlighter-rouge">dockremap</code> user and have Docker create it for you, set the value to <code class="language-plaintext highlighter-rouge">default</code> rather than <code class="language-plaintext highlighter-rouge">testuser</code>.</p> </blockquote> <p>Save the file and restart Docker.</p> </li> <li> <p>If you are using the <code class="language-plaintext highlighter-rouge">dockremap</code> user, verify that Docker created it using the <code class="language-plaintext highlighter-rouge">id</code> command.</p> <div class="highlight"><pre class="highlight" data-language="">$ id dockremap + +uid=112(dockremap) gid=116(dockremap) groups=116(dockremap) +</pre></div> <p>Verify that the entry has been added to <code class="language-plaintext highlighter-rouge">/etc/subuid</code> and <code class="language-plaintext highlighter-rouge">/etc/subgid</code>:</p> <div class="highlight"><pre class="highlight" data-language="">$ grep dockremap /etc/subuid + +dockremap:231072:65536 + +$ grep dockremap /etc/subgid + +dockremap:231072:65536 +</pre></div> <p>If these entries are not present, edit the files as the <code class="language-plaintext highlighter-rouge">root</code> user and assign a starting UID and GID that is the highest-assigned one plus the offset (in this case, <code class="language-plaintext highlighter-rouge">65536</code>). Be careful not to allow any overlap in the ranges.</p> </li> <li> <p>Verify that previous images are not available using the <code class="language-plaintext highlighter-rouge">docker image ls</code> command. The output should be empty.</p> </li> <li> <p>Start a container from the <code class="language-plaintext highlighter-rouge">hello-world</code> image.</p> <div class="highlight"><pre class="highlight" data-language="">$ docker run hello-world +</pre></div> </li> <li> <p>Verify that a namespaced directory exists within <code class="language-plaintext highlighter-rouge">/var/lib/docker/</code> named with the UID and GID of the namespaced user, owned by that UID and GID, and not group-or-world-readable. Some of the subdirectories are still owned by <code class="language-plaintext highlighter-rouge">root</code> and have different permissions.</p> <div class="highlight"><pre class="highlight" data-language="">$ sudo ls -ld /var/lib/docker/231072.231072/ + +drwx------ 11 231072 231072 11 Jun 21 21:19 /var/lib/docker/231072.231072/ + +$ sudo ls -l /var/lib/docker/231072.231072/ + +total 14 +drwx------ 5 231072 231072 5 Jun 21 21:19 aufs +drwx------ 3 231072 231072 3 Jun 21 21:21 containers +drwx------ 3 root root 3 Jun 21 21:19 image +drwxr-x--- 3 root root 3 Jun 21 21:19 network +drwx------ 4 root root 4 Jun 21 21:19 plugins +drwx------ 2 root root 2 Jun 21 21:19 swarm +drwx------ 2 231072 231072 2 Jun 21 21:21 tmp +drwx------ 2 root root 2 Jun 21 21:19 trust +drwx------ 2 231072 231072 3 Jun 21 21:19 volumes +</pre></div> <p>Your directory listing may have some differences, especially if you use a different container storage driver than <code class="language-plaintext highlighter-rouge">aufs</code>.</p> <p>The directories which are owned by the remapped user are used instead of the same directories directly beneath <code class="language-plaintext highlighter-rouge">/var/lib/docker/</code> and the unused versions (such as <code class="language-plaintext highlighter-rouge">/var/lib/docker/tmp/</code> in the example here) can be removed. Docker does not use them while <code class="language-plaintext highlighter-rouge">userns-remap</code> is enabled.</p> </li> </ol> <h2 id="disable-namespace-remapping-for-a-container">Disable namespace remapping for a container</h2> <p>If you enable user namespaces on the daemon, all containers are started with user namespaces enabled by default. In some situations, such as privileged containers, you may need to disable user namespaces for a specific container. See <a href="#user-namespace-known-limitations">user namespace known limitations</a> for some of these limitations.</p> <p>To disable user namespaces for a specific container, add the <code class="language-plaintext highlighter-rouge">--userns=host</code> flag to the <code class="language-plaintext highlighter-rouge">docker container create</code>, <code class="language-plaintext highlighter-rouge">docker container run</code>, or <code class="language-plaintext highlighter-rouge">docker container exec</code> command.</p> <p>There is a side effect when using this flag: user remapping will not be enabled for that container but, because the read-only (image) layers are shared between containers, ownership of the containers filesystem will still be remapped.</p> <p>What this means is that the whole container filesystem will belong to the user specified in the <code class="language-plaintext highlighter-rouge">--userns-remap</code> daemon config (<code class="language-plaintext highlighter-rouge">231072</code> in the example above). This can lead to unexpected behavior of programs inside the container. For instance <code class="language-plaintext highlighter-rouge">sudo</code> (which checks that its binaries belong to user <code class="language-plaintext highlighter-rouge">0</code>) or binaries with a <code class="language-plaintext highlighter-rouge">setuid</code> flag.</p> <h2 id="user-namespace-known-limitations">User namespace known limitations</h2> <p>The following standard Docker features are incompatible with running a Docker daemon with user namespaces enabled:</p> <ul> <li>sharing PID or NET namespaces with the host (<code class="language-plaintext highlighter-rouge">--pid=host</code> or <code class="language-plaintext highlighter-rouge">--network=host</code>).</li> <li>external (volume or storage) drivers which are unaware or incapable of using daemon user mappings.</li> <li>Using the <code class="language-plaintext highlighter-rouge">--privileged</code> mode flag on <code class="language-plaintext highlighter-rouge">docker run</code> without also specifying <code class="language-plaintext highlighter-rouge">--userns=host</code>.</li> </ul> <p>User namespaces are an advanced feature and require coordination with other capabilities. For example, if volumes are mounted from the host, file ownership must be pre-arranged need read or write access to the volume contents.</p> <p>While the root user inside a user-namespaced container process has many of the expected privileges of the superuser within the container, the Linux kernel imposes restrictions based on internal knowledge that this is a user-namespaced process. One notable restriction is the inability to use the <code class="language-plaintext highlighter-rouge">mknod</code> command. Permission is denied for device creation within the container when run by the <code class="language-plaintext highlighter-rouge">root</code> user.</p> +<p><a href="https://docs.docker.com/search/?q=security">security</a>, <a href="https://docs.docker.com/search/?q=namespaces">namespaces</a></p> +<div class="_attribution"> + <p class="_attribution-p"> + © 2019 Docker, Inc.<br>Licensed under the Apache License, Version 2.0.<br>Docker and the Docker logo are trademarks or registered trademarks of Docker, Inc. in the United States and/or other countries.<br>Docker, Inc. and other parties may also have trademark rights in other terms used herein.<br> + <a href="https://docs.docker.com/engine/security/userns-remap/" class="_attribution-link">https://docs.docker.com/engine/security/userns-remap/</a> + </p> +</div> |
