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
目录
相关文章
|
27天前
|
Docker 容器
进入Docker容器中
进入Docker容器中
34 2
|
1天前
|
存储 Kubernetes Docker
Kubernetes(K8S)集群管理Docker容器(概念篇)
Kubernetes(K8S)集群管理Docker容器(概念篇)
|
1天前
|
存储 Ubuntu 安全
Docker容器常用命令
Docker容器常用命令
|
7天前
|
存储 运维 监控
构建高效稳定的Docker容器监控体系
【4月更文挑战第18天】 在现代微服务架构中,Docker容器已成为部署和运行应用的标准环境。随之而来的挑战是如何有效监控这些容器的性能与健康状况,确保系统的稳定性和可靠性。本文将探讨构建一个高效稳定的Docker容器监控体系的关键技术和方法,包括日志管理、性能指标收集以及异常检测机制,旨在为运维人员提供实用的指导和建议。
12 0
|
16天前
|
Linux Docker 容器
docker 容器常用命令
docker 容器常用命令
13 0
|
16天前
|
Linux Shell 虚拟化
linux 部署docker容器虚拟化平台(二)--------docker 镜像制作方法
linux 部署docker容器虚拟化平台(二)--------docker 镜像制作方法
27 0
|
16天前
|
存储 Linux Shell
centos 部署docker容器 安装 、基本使用方法(一)
centos 部署docker容器 安装 、基本使用方法(一)
29 0
|
24天前
|
Kubernetes 网络协议 Docker
Docker 容器的DNS
Docker 容器的DNS
28 1
|
27天前
|
关系型数据库 MySQL Nacos
【深入浅出Nacos原理及调优】「实战开发专题」采用Docker容器进行部署和搭建Nacos服务以及“坑点”
【深入浅出Nacos原理及调优】「实战开发专题」采用Docker容器进行部署和搭建Nacos服务以及“坑点”
48 1
|
28天前
|
NoSQL 关系型数据库 MySQL
安装Docker&镜像容器操作&使用Docker安装部署MySQL,Redis,RabbitMQ,Nacos,Seata,Minio
安装Docker&镜像容器操作&使用Docker安装部署MySQL,Redis,RabbitMQ,Nacos,Seata,Minio
359 1