docker 容器安全问题实验

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: docker 容器安全问题实验

问题演示



使用 root 账号创建一个临时文件 a.txt:


[root@localhost ~]# echo "aaa" > /root/tmp/a.txt
[root@localhost ~]# cat /root/tmp/a.txt 
aaa
复制代码


确认 work 账号没有 a.txt 的权限:


[work@localhost ~]$ cat /root/tmp/a.txt
cat: /root/tmp/a.txt: Permission denied
复制代码


使用下面命令启动一个redis容器并挂载数据卷 /root/tmp :


# 注意这里是work账号
[work@localhost ~]$ docker run -d -v /root/tmp:/root/tmp  redis:6.0.10
f709b9d2ef3acd8edd525ba6a47246976715d720c047c868bea0631d51f4ab54docker run -d -v /root/tmp:/root/tmp  redis:6.0.10
复制代码


进入刚才创建的 redis 容器并修改 a.txt 文件:


# 注意这里也是work账号
[work@localhost ~]$ docker exec -it f70 bash
root@f709b9d2ef3a:/data# echo "bbb" >> /root/tmp/a.txt 
root@f709b9d2ef3a:/data# cat /root/tmp/a.txt 
aaa
bbb
复制代码


回到宿主机上,使用 root 账号确认 /root/tmp/a.txt 文件内容:


[root@localhost ~]# cat /root/tmp/a.txt 
aaa
bbb
复制代码


可以看到一个普通的 work 账号,通过docker容器提权操作了一个没有权限的文档。


原理



使用work账号进入刚才的redis容器然后保持:


[work@localhost ~]$ docker exec -it f70 bash
root@f709b9d2ef3a:/data# id
uid=0(root) gid=0(root) groups=0(root)
复制代码


在宿主机使用root账号查看一下容器的bash进程


[root@localhost ~]# docker top f70
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
systemd+            13516               13497               0                   04:18               ?                   00:00:03            redis-server *:6379
root                13679               13497               0                   04:32               pts/0               00:00:00            bash
复制代码


在宿主机确认13679进程的user是root


[root@localhost ~]# ps -ef | grep 13679
root     13679 13497  0 04:32 pts/0    00:00:00 bash
复制代码


可以看到work账号进入容器bash程序的时候,切换成root账号,所以权限得到提升。如果绑定了root的目录,则就可以对root目录进行操作。


这是因为我们的docker服务是使用root账号启动的:


[root@localhost ~]# ps -ef | grep dockerd
root     12694     1  0 03:59 ?        00:00:01 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
复制代码


容器用户



在宿主机查看redis进程, 可以知道宿主机上redis进程的username是 polkitd :


$ ps -ef | grep redis
polkitd  12876 12857  0 14:17 ?        00:00:00 redis-server *:6379
复制代码


也可以使用 docker top 查看:


$ docker top dca
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
polkitd             12876               12857               1                   14:17               ?                   00:00:00            redis-server *:6379
复制代码


查看宿主机上polkitd账号的uid:


$ id polkitd
uid=999(polkitd) gid=998(polkitd) 组=998(polkitd)
复制代码


进入容器查看容器内用户:


root@5be8ce78244c:/data# gosu redis bash  # 使用gosu切换到redis账号
redis@5be8ce78244c:/data$ id
uid=999(redis) gid=999(redis) groups=999(redis)
复制代码


这里可以发现容器内redis和宿主机polkitd的uid是一致的。


redis:6.0.10镜像没有安装 ps, 可以进入容器后执行 apt-get update && apt-get install procps 安装。然后查看redis进程的uid:


root@6c54c348686d:/data# ps -ef
UID         PID   PPID  C STIME TTY          TIME CMD
redis         1      0  0 08:08 ?        00:00:00 redis-server *:6379
root         26      0  0 08:09 pts/0    00:00:00 bash
root        350     26  0 08:15 pts/0    00:00:00 ps -ef
复制代码


确认容器内是使用redis账号启动的服务,服务进程号是 1。还可以在宿主机上确认redis账号不存在:


$ id redis
id: redis: no such user
复制代码


我们可以得出结论1: 容器用户uid和宿主机用户的uid相同,名称可以不同


指定容器用户



容器可以指定用户。编写 Dockerfile


FROM redis:6.0.10
RUN groupadd -r test1 && useradd -r -g test1 test1  # 创建一下test1用户
复制代码


编译镜像


docker build -t redis:redis_test .
复制代码


运行自定义的redis镜像, 运行时需要指定用户


docker run -d --user test1 redis:redis_test
复制代码


docker run -u 参数帮助:  --user string   Username or UID (format: <name|uid>[:<group|gid>])


在宿主机上查看redis进程信息, 发现是systemd-bus-proxy创建的进程:


$ ps -axo user:20,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,comm | grep redis
systemd-bus-proxy    15429  0.1  0.0  52796 11916 ?        Ssl  16:24:01 00:00:01 redis-server
复制代码


ps -ef 中如果username超过8个字符,会显示成 systemd+, 所以上面命令要使用 use:20 格式化一下长度


看看systemd-bus-proxy的uid


$ id systemd-bus-proxy
uid=998(systemd-bus-proxy) gid=996(systemd-bus-proxy) groups=996(systemd-bus-proxy)
复制代码


进入容器查看:


$ docker exec -it 680 bash
test1@680ccf686e5f:/data$ id  # 注意当前用户变成了test1,uid=998
uid=998(test1) gid=998(test1) groups=998(test1)
复制代码


使用test1运行的容器没有权限安装包,使用下面方式确认进程的uid=998


$ cat /proc/1/status | grep id
Tgid: 1
Ngid: 0
Pid:  1
PPid: 0
TracerPid:  0
Uid:  998 998 998 998
Gid:  998 998 998 998
复制代码


docker的机制是容器的id=1的进程是主进程。


我们可以得出结论2: 容器用户可以在镜像中创建,在container运行时使用--user参数指定


gosu切换用户



容器可以指定用户,自然涉及到切换用户的问题,宿主机上使用 su , 容器内建议 gosu,下面是Docker — 从入门到实践中的内容:


如果以 root 执行的脚本,在执行期间希望改变身份,比如希望以某个已经建立 好的用户来运行某个服务进程,不要使用 su 或者 sudo ,这些都需要比较麻烦 的配置,而且在 TTY 缺失的环境下经常出错。


redis:6.0.10的 Dockerfile 源码中可以看到创建了redis=999的账号:


# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
RUN groupadd -r -g 999 redis && useradd -r -g redis -u 999 redis
复制代码


重点是 docker-entrypoint.sh 部分:


root@6c54c348686d:/data# whereis docker-entrypoint.sh
docker-entrypoint: /usr/local/bin/docker-entrypoint.sh
root@6c54c348686d:/data# cat /usr/local/bin/docker-entrypoint.sh
#!/bin/sh
set -e
# first arg is `-f` or `--some-option`
# or first arg is `something.conf`
if [ "${1#-}" != "$1" ] || [ "${1%.conf}" != "$1" ]; then
  set -- redis-server "$@"
fi
# allow the container to be started with `--user`
if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
  find . \! -user redis -exec chown redis '{}' +
  exec gosu redis "$0" "$@"  # 使用redis账号启动redis-server
fi
exec "$@"
复制代码


所以默认的redis:6.0.10显示uid=999。


下面是gosu官方的日志:


$ 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
复制代码


sudo会作为被授权的命令的父进程一直存在,直到该命令退出,gosu则没有这个问题。


版本



本次使用使用的相关版本:


NAME="CentOS Linux"
VERSION="7 (Core)"
...
Client: Docker Engine - Community
 Version:           20.10.7
 API version:       1.41
...
Server: Docker Engine - Community
 Engine:
  Version:          20.10.7
  API version:      1.41 (minimum version 1.12)
  ...
复制代码


清理



可以使用下面命令清理实验环境


docker ps -aq | xargs docker  stop  # 关闭容器
docker container prune  # 清理容器
docker rmi redis:redis_test  # 删除镜像
docker image prune  # 清理镜像
复制代码


参考链接




相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
打赏
0
0
0
0
7
分享
相关文章
docker容器为啥会开机自启动
通过配置适当的重启策略,Docker容器可以在主机系统重启后自动启动。这对于保持关键服务的高可用性和自动恢复能力非常有用。选择适合的重启策略(如 `always`或 `unless-stopped`),可以确保应用程序在各种情况下保持运行。理解并配置这些策略是确保Docker容器化应用可靠性的关键。
268 93
Docker+consul容器服务的更新与发现
通过本文的介绍,我们详细探讨了如何结合Docker和Consul来实现容器服务的更新与发现。通过Consul的服务注册和发现功能,可以高效地管理和监控容器化服务,确保系统的高可用性和可扩展性。希望本文能帮助您在实际项目中更好地应用Docker和Consul,提高系统的可靠性和管理效率。
58 23
ubuntu22 编译安装docker,和docker容器方式安装 deepseek
本脚本适用于Ubuntu 22.04,主要功能包括编译安装Docker和安装DeepSeek模型。首先通过Apt源配置安装Docker,确保网络稳定(建议使用VPN)。接着下载并配置Docker二进制文件,创建Docker用户组并设置守护进程。随后拉取Debian 12镜像,安装系统必备工具,配置Ollama模型管理器,并最终部署和运行DeepSeek模型,提供API接口进行交互测试。
274 15
《docker基础篇:3.Docker常用命令》包括帮助启动类命令、镜像命令、有镜像才能创建容器,这是根本前提(下载一个CentOS或者ubuntu镜像演示)、容器命令、小总结
《docker基础篇:3.Docker常用命令》包括帮助启动类命令、镜像命令、有镜像才能创建容器,这是根本前提(下载一个CentOS或者ubuntu镜像演示)、容器命令、小总结
187 6
《docker基础篇:3.Docker常用命令》包括帮助启动类命令、镜像命令、有镜像才能创建容器,这是根本前提(下载一个CentOS或者ubuntu镜像演示)、容器命令、小总结
docker 是什么?docker初认识之如何部署docker-优雅草后续将会把产品发布部署至docker容器中-因此会出相关系列文章-优雅草央千澈
Docker 是一个开源的容器化平台,允许开发者将应用程序及其依赖项打包成标准化单元(容器),确保在任何支持 Docker 的操作系统上一致运行。容器共享主机内核,提供轻量级、高效的执行环境。本文介绍如何在 Ubuntu 上安装 Docker,并通过简单步骤验证安装成功。后续文章将探讨使用 Docker 部署开源项目。优雅草央千澈 源、安装 Docker 包、验证安装 - 适用场景:开发、测试、生产环境 通过以上步骤,您可以在 Ubuntu 系统上成功安装并运行 Docker,为后续的应用部署打下基础。
100 8
docker 是什么?docker初认识之如何部署docker-优雅草后续将会把产品发布部署至docker容器中-因此会出相关系列文章-优雅草央千澈
docker容器为啥会开机自启动
通过配置适当的重启策略,Docker容器可以在主机系统重启后自动启动。这对于保持关键服务的高可用性和自动恢复能力非常有用。选择适合的重启策略(如 `always`或 `unless-stopped`),可以确保应用程序在各种情况下保持运行。理解并配置这些策略是确保Docker容器化应用可靠性的关键。
72 17
《docker基础篇:1.Docker简介》,包括Docker是什么、容器与虚拟机比较、能干嘛、去哪下
《docker基础篇:1.Docker简介》,包括Docker是什么、容器与虚拟机比较、能干嘛、去哪下
129 12
容器化时代的领航者:Docker 和 Kubernetes 云原生时代的黄金搭档
Docker 是一种开源的应用容器引擎,允许开发者将应用程序及其依赖打包成可移植的镜像,并在任何支持 Docker 的平台上运行。其核心概念包括镜像、容器和仓库。镜像是只读的文件系统,容器是镜像的运行实例,仓库用于存储和分发镜像。Kubernetes(k8s)则是容器集群管理系统,提供自动化部署、扩展和维护等功能,支持服务发现、负载均衡、自动伸缩等特性。两者结合使用,可以实现高效的容器化应用管理和运维。Docker 主要用于单主机上的容器管理,而 Kubernetes 则专注于跨多主机的容器编排与调度。尽管 k8s 逐渐减少了对 Docker 作为容器运行时的支持,但 Doc
198 5
容器化时代的领航者:Docker 和 Kubernetes 云原生时代的黄金搭档
入门级容器技术解析:Docker和K8s的区别与关系
本文介绍了容器技术的发展历程及其重要组成部分Docker和Kubernetes。从传统物理机到虚拟机,再到容器化,每一步都旨在更高效地利用服务器资源并简化应用部署。容器技术通过隔离环境、减少依赖冲突和提高可移植性,解决了传统部署方式中的诸多问题。Docker作为容器化平台,专注于创建和管理容器;而Kubernetes则是一个强大的容器编排系统,用于自动化部署、扩展和管理容器化应用。两者相辅相成,共同推动了现代云原生应用的快速发展。
265 11

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等