简介
处理 Docker 中的权限是管理容器化应用程序的一个关键方面。本教程将指导你了解 Docker 文件权限、在 Docker 容器中设置权限,并探索权限管理的最佳实践。学完本教程后,你将掌握在基于 Docker 的项目中有效处理权限的知识。
在本实验中,你将学习 Docker 容器权限的工作原理、如何在容器中创建和使用非 root 用户,以及如何在主机和容器之间共享数据时管理权限。
处理 Docker 中的权限是管理容器化应用程序的一个关键方面。本教程将指导你了解 Docker 文件权限、在 Docker 容器中设置权限,并探索权限管理的最佳实践。学完本教程后,你将掌握在基于 Docker 的项目中有效处理权限的知识。
在本实验中,你将学习 Docker 容器权限的工作原理、如何在容器中创建和使用非 root 用户,以及如何在主机和容器之间共享数据时管理权限。
在 Docker 中,理解权限的工作原理是维护容器安全的基础。让我们先从探究 Docker 容器的默认权限设置开始。
默认情况下,Docker 在容器内以 root
用户身份运行进程。如果容器被攻破,这可能会导致潜在的安全问题。为了观察这种行为,让我们创建一个简单的容器并检查运行进程的用户。
首先,确保你位于项目目录中:
cd ~/project
现在,运行一个基本的 Ubuntu 容器并检查当前用户:
docker run -it --rm ubuntu:22.04 whoami
你应该会看到以下输出:
root
这证实了 Docker 默认使用 root
用户。现在,让我们检查用户 ID (UID) 和组 ID (GID):
docker run -it --rm ubuntu:22.04 id
输出应该类似于:
uid=0(root) gid=0(root) groups=0(root)
uid=0
和 gid=0
表明容器以 root 用户和组的身份运行,这意味着它们对容器内的所有资源拥有完全访问权限。
让我们来研究一下 Docker 容器内的文件权限是如何工作的。在容器内创建一个简单的文件并检查其权限。
首先,创建一个在后台运行的容器:
docker run -d --name permissions-demo ubuntu:22.04 sleep 3600
现在,让我们在这个容器内执行命令来创建一个文件并检查其权限:
docker exec permissions-demo touch /test-file
docker exec permissions-demo ls -l /test-file
你应该会看到类似于以下的输出:
-rw-r--r-- 1 root root 0 May 15 12:34 /test-file
注意,该文件由 root
用户和组拥有。这表明 Docker 容器内默认创建的所有文件都由 root 用户拥有。
让我们再创建一个目录并检查其权限:
docker exec permissions-demo mkdir /test-directory
docker exec permissions-demo ls -ld /test-directory
输出应该类似于:
drwxr-xr-x 2 root root 4096 May 15 12:35 /test-directory
同样,该目录由 root
用户和组拥有。
在进入下一步之前,让我们清理这个容器:
docker stop permissions-demo
docker rm permissions-demo
这展示了 Docker 在用户权限方面的默认行为。在下一步中,我们将学习如何在 Docker 容器中创建和使用非 root 用户以提高安全性。
在 Docker 容器内以 root 用户身份运行应用程序存在安全风险。如果攻击者攻破了以 root 身份运行的容器,他们有可能获得主机系统的更高权限。在这一步中,我们将学习如何在 Docker 容器中创建和使用非 root 用户。
让我们创建一个 Dockerfile,定义一个非 root 用户并将其设置为运行命令的默认用户。
首先,为我们的项目创建一个新目录:
mkdir -p ~/project/non-root-user
cd ~/project/non-root-user
现在,使用 nano
文本编辑器创建一个 Dockerfile:
nano Dockerfile
将以下内容添加到 Dockerfile 中:
FROM ubuntu:22.04
## Create a new user called 'appuser' with user ID 1000
RUN useradd -m -u 1000 appuser
## Create a directory for the application and set ownership
RUN mkdir -p /app && chown -R appuser:appuser /app
## Set the working directory to /app
WORKDIR /app
## Switch to the non-root user
USER appuser
## Create a test file
RUN touch test-file.txt
## Command to run when the container starts
CMD ["bash", "-c", "echo 'Running as user:' && whoami && echo 'File ownership:' && ls -l test-file.txt && tail -f /dev/null"]
按 Ctrl+O
保存文件,然后按 Enter
,再按 Ctrl+X
退出编辑器。
这个 Dockerfile 完成了以下操作:
appuser
、UID 为 1000 的新用户/app
现在,让我们根据 Dockerfile 构建 Docker 镜像:
docker build -t non-root-image .
你应该会看到表明 Docker 正在构建镜像的输出。构建完成后,使用这个镜像运行一个容器:
docker run --name non-root-container -d non-root-image
现在,让我们查看容器的输出:
docker logs non-root-container
你应该会看到类似于以下的输出:
Running as user:
appuser
File ownership:
-rw-r--r-- 1 appuser appuser 0 May 15 12:45 test-file.txt
这证实了容器以 appuser
用户身份运行,并且测试文件由 appuser
拥有。
让我们连接到正在运行的容器并探究非 root 用户的权限:
docker exec -it non-root-container bash
现在你应该以 appuser
身份进入容器内部。让我们验证一下:
whoami
你应该会看到:
appuser
检查用户 ID 和组 ID:
id
输出应该类似于:
uid=1000(appuser) gid=1000(appuser) groups=1000(appuser)
现在,尝试在根目录下创建一个文件:
touch /root-test-file
你应该会看到权限被拒绝的错误:
touch: cannot touch '/root-test-file': Permission denied
这是因为非 root 用户在根目录下没有写入权限。不过,我们的用户可以在 /app
目录下写入文件:
touch /app/user-test-file
ls -l /app/user-test-file
你应该会看到新文件由 appuser
拥有:
-rw-r--r-- 1 appuser appuser 0 May 15 12:50 /app/user-test-file
退出容器的 shell:
exit
在进入下一步之前,让我们清理这个容器:
docker stop non-root-container
docker rm non-root-container
这一步展示了如何在 Docker 容器中创建和使用非 root 用户。使用非 root 用户可以通过限制应用程序的可用权限来提高容器化应用程序的安全性。
Docker 卷允许你在主机和容器之间共享数据。然而,在使用卷时正确管理权限对于避免问题至关重要。在这一步中,我们将学习如何在使用卷挂载时处理权限问题。
Docker 卷挂载的主要挑战在于,容器内的用户 ID 可能与主机系统上的用户 ID 不匹配。这可能会在访问挂载卷中的文件时导致权限问题。
让我们通过一个简单的示例来演示这个问题。
首先,在主机上创建一个新目录,我们将把它挂载到容器中:
mkdir -p ~/project/host-data
cd ~/project/host-data
在这个目录中创建一个测试文件:
echo "This is a test file created on the host" > host-file.txt
检查这个文件的所有权:
ls -l host-file.txt
你应该会看到该文件由 labex
用户(你在主机上的当前用户)拥有:
-rw-r--r-- 1 labex labex 39 May 15 13:00 host-file.txt
现在,让我们运行一个挂载这个目录的容器,并尝试修改该文件:
docker run -it --rm -v ~/project/host-data:/container-data ubuntu:22.04 bash
现在你已经进入容器内部。让我们检查挂载文件的所有权:
ls -l /container-data/host-file.txt
你可能会看到类似于以下的输出:
-rw-r--r-- 1 1000 1000 39 May 15 13:00 /container-data/host-file.txt
注意,文件显示的是数字 ID 而不是用户名,因为容器不知道主机上的 labex
用户。
尝试在挂载目录中创建一个新文件:
echo "This is a test file created in the container" > /container-data/container-file.txt
现在,检查这个新文件的所有权:
ls -l /container-data/container-file.txt
你应该会看到:
-rw-r--r-- 1 root root 47 May 15 13:05 /container-data/container-file.txt
该文件在容器内由 root
用户拥有(因为我们默认以 root 身份运行)。
退出容器:
exit
现在,检查主机上这两个文件的所有权:
ls -l ~/project/host-data/
你会看到从容器内部创建的文件在主机上由 root
用户拥有:
-rw-r--r-- 1 root root 47 May 15 13:05 container-file.txt
-rw-r--r-- 1 labex labex 39 May 15 13:00 host-file.txt
如果主机上的非 root 用户需要访问或修改这些文件,这可能会导致权限问题。
有几种方法可以解决卷权限问题。让我们探讨一些常见的方法。
为这个示例创建一个新目录:
mkdir -p ~/project/volume-permissions
cd ~/project/volume-permissions
创建一个新的 Dockerfile:
nano Dockerfile
添加以下内容:
FROM ubuntu:22.04
## Create a user with the same UID as the host user
RUN useradd -m -u 1000 appuser
## Create app directory and set ownership
RUN mkdir -p /app/data && chown -R appuser:appuser /app
## Set working directory
WORKDIR /app
## Switch to appuser
USER appuser
## Command to run
CMD ["bash", "-c", "echo 'I can write to the mounted volume' > /app/data/test.txt && tail -f /dev/null"]
保存并退出编辑器。
构建镜像:
docker build -t volume-permissions-image .
创建一个用于测试的主机目录:
mkdir -p ~/project/volume-permissions/host-data
运行一个挂载卷的容器:
docker run -d --name volume-test -v ~/project/volume-permissions/host-data:/app/data volume-permissions-image
过一会儿,检查主机上创建的文件的所有权:
ls -l ~/project/volume-permissions/host-data/
你应该会看到该文件由 UID 为 1000 的用户拥有,这应该与你主机用户的 UID 匹配:
-rw-r--r-- 1 labex labex 35 May 15 13:15 test.txt
这种方法之所以有效,是因为我们在容器中创建了一个与主机用户 UID 相同的用户。
--user
标志另一种方法是使用 --user
标志来指定运行容器时使用的用户 ID。
首先,清理之前的容器:
docker stop volume-test
docker rm volume-test
现在使用 --user
标志运行一个容器:
docker run -d --name user-flag-test --user "$(id -u):$(id -g)" -v ~/project/volume-permissions/host-data:/data ubuntu:22.04 bash -c "echo 'Created with --user flag' > /data/user-flag-test.txt && sleep 3600"
这个命令使用你当前的用户 ID 和组 ID 运行容器。
检查新文件的所有权:
ls -l ~/project/volume-permissions/host-data/user-flag-test.txt
你应该会看到该文件由你的主机用户拥有:
-rw-r--r-- 1 labex labex 23 May 15 13:20 user-flag-test.txt
在进入下一步之前,让我们进行清理:
docker stop user-flag-test
docker rm user-flag-test
这些方法展示了如何在使用 Docker 卷时管理权限。通过使容器和主机之间的用户 ID 匹配,你可以避免在它们之间共享数据时出现权限问题。
让我们通过创建一个更实际的 Docker 容器示例,将所学的所有知识付诸实践,该示例遵循权限最佳实践。我们将创建一个简单的 Web 应用程序容器,遵循权限方面的安全最佳实践。
首先,为我们的示例创建一个新目录:
mkdir -p ~/project/secure-app
cd ~/project/secure-app
让我们创建一个简单的 Web 应用程序。首先,创建一个 app.py
文件:
nano app.py
添加以下 Python 代码以创建一个简单的 Flask Web 服务器:
from flask import Flask
import os
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello from a secure container!'
@app.route('/whoami')
def whoami():
return os.popen('id').read()
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
保存并退出编辑器。
现在,为 Python 依赖项创建一个 requirements.txt
文件:
nano requirements.txt
添加以下内容:
flask==2.0.1
保存并退出编辑器。
现在,创建一个遵循权限最佳实践的 Dockerfile:
nano Dockerfile
添加以下内容:
FROM python:3.10-slim
## Create a non-root user to run the application
RUN groupadd -g 1000 appgroup \
&& useradd -u 1000 -g appgroup -s /bin/bash -m appuser
## Set working directory and create necessary directories
WORKDIR /app
## Copy requirements first to leverage Docker cache
COPY requirements.txt .
## Install dependencies as root
RUN pip install --no-cache-dir -r requirements.txt
## Copy application code
COPY app.py .
## Create a data directory that the application can write to
RUN mkdir -p /app/data \
&& chown -R appuser:appgroup /app
## Set proper permissions
RUN chmod -R 755 /app
## Switch to non-root user
USER appuser
## Expose the port the app will run on
EXPOSE 8080
## Command to run the application
CMD ["python", "app.py"]
保存并退出编辑器。
让我们构建 Docker 镜像:
docker build -t secure-web-app .
现在,运行容器:
docker run -d --name secure-app -p 8080:8080 secure-web-app
让我们测试该应用程序。首先,检查容器是否正在运行:
docker ps
你应该会在列表中看到你的 secure-app
容器。现在,使用 curl
测试应用程序:
curl http://localhost:8080/
你应该会看到:
Hello from a secure container!
让我们检查应用程序以哪个用户身份运行:
curl http://localhost:8080/whoami
你应该会看到类似于以下的输出:
uid=1000(appuser) gid=1000(appgroup) groups=1000(appgroup)
这证实了我们的应用程序以非 root 用户 appuser
身份运行。
让我们分析一下我们的 Dockerfile 在安全方面的改进:
appuser
) 来运行应用程序,降低了容器被攻破时的风险。这些实践有助于确保即使攻击者设法利用了应用程序中的漏洞,他们对容器和主机系统的访问也将受到限制。
让我们为我们的安全应用程序添加一个卷,以演示如何正确处理卷的权限。
首先,停止并移除现有的容器:
docker stop secure-app
docker rm secure-app
在主机上创建一个数据目录:
mkdir -p ~/project/secure-app/host-data
设置主机目录的正确权限:
sudo chown 1000:1000 ~/project/secure-app/host-data
现在,运行挂载了卷的容器:
docker run -d --name secure-app-with-volume \
-p 8080:8080 \
-v ~/project/secure-app/host-data:/app/data \
secure-web-app
让我们连接到容器并在挂载的卷中创建一个文件:
docker exec -it secure-app-with-volume bash
现在,在容器内部,在挂载的卷中创建一个测试文件:
echo "Test file created from inside the container" > /app/data/container-file.txt
检查文件的所有权:
ls -l /app/data/container-file.txt
你应该会看到该文件由 appuser
拥有:
-rw-r--r-- 1 appuser appgroup 43 May 15 13:30 /app/data/container-file.txt
退出容器:
exit
现在,检查主机上文件的所有权:
ls -l ~/project/secure-app/host-data/container-file.txt
你应该会看到该文件由 UID 为 1000 的用户拥有(对应于你的主机用户):
-rw-r--r-- 1 labex labex 43 May 15 13:30 container-file.txt
这表明,通过正确的权限配置,容器内挂载卷中创建的文件在主机上也会有正确的所有权。
在结束之前,让我们进行清理:
docker stop secure-app-with-volume
docker rm secure-app-with-volume
通过遵循这些 Docker 权限的最佳实践,你可以创建安全、可靠的容器化应用程序,这些应用程序能够在容器内部以及与主机系统共享数据时正确管理文件权限。
在本次实验中,你学习了在 Docker 容器中管理权限的重要技术:
通过在你的 Docker 项目中应用这些技术,你可以显著提高容器化应用程序的安全性和可靠性。请记住,以最小必要权限运行容器是一项基本的安全原则,应该始终遵循。
一些关键要点如下:
这些实践将帮助你构建更安全的 Docker 容器,并避免常见的与权限相关的问题。