docker 容器安全问题实验

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器镜像服务 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
目录
相关文章
|
8天前
|
Kubernetes Cloud Native Docker
云原生时代的容器化实践:Docker和Kubernetes入门
【10月更文挑战第37天】在数字化转型的浪潮中,云原生技术成为企业提升敏捷性和效率的关键。本篇文章将引导读者了解如何利用Docker进行容器化打包及部署,以及Kubernetes集群管理的基础操作,帮助初学者快速入门云原生的世界。通过实际案例分析,我们将深入探讨这些技术在现代IT架构中的应用与影响。
33 2
|
5天前
|
运维 Cloud Native 虚拟化
一文吃透云原生 Docker 容器,建议收藏!
本文深入解析云原生Docker容器技术,涵盖容器与Docker的概念、优势、架构设计及应用场景等,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
一文吃透云原生 Docker 容器,建议收藏!
|
6天前
|
缓存 监控 开发者
掌握Docker容器化技术:提升开发效率的利器
在现代软件开发中,Docker容器化技术成为提升开发效率和应用部署灵活性的重要工具。本文介绍Docker的基本概念,并分享Dockerfile最佳实践、容器网络配置、环境变量和秘密管理、容器监控与日志管理、Docker Compose以及CI/CD集成等技巧,帮助开发者更高效地利用Docker。
|
8天前
|
监控 持续交付 Docker
Docker 容器化部署在微服务架构中的应用有哪些?
Docker 容器化部署在微服务架构中的应用有哪些?
|
8天前
|
监控 持续交付 Docker
Docker容器化部署在微服务架构中的应用
Docker容器化部署在微服务架构中的应用
|
8天前
|
安全 持续交付 Docker
微服务架构和 Docker 容器化部署的优点是什么?
微服务架构和 Docker 容器化部署的优点是什么?
|
10天前
|
运维 开发者 Docker
Docker Compose:简化容器化应用的部署与管理
Docker Compose:简化容器化应用的部署与管理
|
10天前
|
NoSQL Redis Docker
【赵渝强老师】使用Docker Compose管理容器
Docker Compose 通过 YAML 文件管理多个容器,简化复杂系统的部署和管理。本文介绍了 Docker Compose 的基本概念,并通过一个包含 Redis DB 和 Python Web 模块的示例,展示了如何使用 Docker Compose 部署和管理多容器应用。手动部署和 Docker Compose 部署的对比突显了 Docker Compose 在系统复杂度增加时的优势。
|
14天前
|
关系型数据库 MySQL API
|
10天前
|
运维 持续交付 Docker
深入理解Docker容器化技术
深入理解Docker容器化技术