Dockerfile之gosu浅析

权限分析

  • Docker容器运行的时候,如果没有专门指定user, 默认以root用户运行。我们镜像里没有指定user,容器里的执行用户的id是0,输出文件的权限也是0
# 以ROOT用户执行docker查看权限情况如下,发现容器里面文件宿主都是ROOT用户
[root@boy dockerfile]# docker run -it --rm centos:7 /bin/bash -c "echo 1 > /root/text && ls -al /root"
total 28
dr-xr-x---. 1 root root   18 Apr 25 05:44 .
drwxr-xr-x  1 root root   18 Apr 25 05:44 ..
-rw-r--r--. 1 root root   18 Dec 29  2013 .bash_logout
-rw-r--r--. 1 root root  176 Dec 29  2013 .bash_profile
-rw-r--r--. 1 root root  176 Dec 29  2013 .bashrc
-rw-r--r--. 1 root root  100 Dec 29  2013 .cshrc
-rw-r--r--. 1 root root  129 Dec 29  2013 .tcshrc
-rw-------. 1 root root 3416 Nov 13  2020 anaconda-ks.cfg
-rw-r--r--  1 root root    2 Apr 25 05:44 text

# 以ROOT用户来执行进程,可见也是ROOT身份
[root@boy dockerfile]# docker run -it --rm centos:7 /bin/bash -c "ps -aux"
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.0  0.0  51728  1544 pts/0    Rs+  06:27   0:00 ps -aux

gosu

  • Docker容器中运行的进程,如果以root身份运行话会有安全隐患,该进程拥有容器内的全部权限,更可怕的是如果有数据卷映射到宿主机,那么通过该容器就能操作宿主机的文件夹了,一旦该容器的进程有漏洞被外部利用后果是很严重的,因此,容器内使用非root账号运行进程才是安全的方式,这也是我们在制作镜像时要注意的地方
  • susudo具有非常奇怪且经常令人讨厌的TTY和信号转发行为的问题。susudo的设置和使用也有些复杂(特别是在sudo的情况下),虽然它们有很大的表达力,但是如果您所需要的只是“以特定用户身份运行特定应用程序”,那么它们将不再那么适合
  • 处理完用户/组后,我们将切换到指定用户,然后执行指定的进程,gosu本身不再驻留或完全不在进程生命周期中。这避免了信号传递和TTY的所有问题
  • 不要在容器中使用sudo命令,因为sudo的执行机制问题,如下所示,我们在启动容器时执行了sudo ps -ef命令,发现我们命名只执行了一条命令,但是竟然会有2个进程,请注意PID,真正执行ps -ef的命令的PID是7,而不是1,这回导致当前进程无法接受Unix的SIGNAL
[root@jiayu ~]# docker run -it --rm ubuntu su -c 'exec ps -aux'
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   4484  1612 pts/0    Ss+  14:31   0:00 su -c exec ps -
root         7  0.0  0.0   5888  1412 pts/0    R+   14:31   0:00 ps -aux

# 下面二个实例,不一定带sudo命令
[root@jiayu ~]# 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

#  可见使用gosu后,切换身份后,且只有一个进程
[root@jiayu ~]# 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
# 查看dockerfile
# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
RUN groupadd -r redis && useradd -r -g redis redis
...............................................
RUN mkdir /data && chown redis:redis /data
VOLUME /data
WORKDIR /data
COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]
EXPOSE 6379
CMD ["redis-server"]
# 查看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" "$@"
fi

exec "$@"

注意上面的代码,我们来分析一下:

  1. 假设启动容器的命令是docker run -it redis redis-server /etc/redis.conf
  2. 容器启动后会执行docker-entrypoint.sh脚本,此时的账号是root,此时对于容器来说接收到命令为:docker-entrypoint.sh redis-server /etc/redis.conf
  3. exec gosu redis $0 $@
# 这条命令当于以redis身份执行如下命令
	docker-entrypoint.sh redis-server /etc/redis.conf
	
# exec
	替换当前的shell,且不会生成新的进程保证了gosu redis “$0"@"对应的进程ID为1
	此时运行命令为:docker-entrypoint.sh redis-server /etc/redis.conf
  1. 当切换成redis用户后会执行如下命令
# exec $@
	redis-server /etc/redis.conf

gosu实例

[root@boy dockerfile]# cat dockerfile 
FROM debian:sid-slim
RUN groupadd -r test && useradd -r -g test test
COPY docker-entrypoint.sh /usr/bin/
RUN apt-get update && apt-get install -y --no-install-recommends procps gosu && rm -rf /var/lib/apt/lists/*

ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["ps -aux && sleep 36000"]
[root@boy dockerfile]# cat docker-entrypoint.sh 
#!/bin/bash
if [ "$(id -u)" == "0" ]; then
	exec gosu test "$0" "$@"
fi

exec $@
# 构建镜像
[root@boy dockerfile]# docker build -t gosu:1.0 .

# 启动容器,可以发现目前容器内的进程都是以test身份运行了,且只有一个进程,不存在sudo,su导致生成二个进程
[root@boy dockerfile]# docker run -it --rm gosu:1.0 ps -aux
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
test          1  4.0  0.0   6960  1572 pts/0    Rs+  07:34   0:00 ps -aux

总结

  • gosu启动命令时只有一个进程,所以docker容器启动时使用gosu,那么该进程可以做到PID等于1
  • sudo启动命令时先创建sudo进程,然后该进程作为父进程去创建子进程,1号PID被sudo进程占据

综上所述,在docker的entrypoint中有如下建议:

  • 创建group和普通账号,不要使用root账号启动进程
  • 如果普通账号权限不够用,建议使用gosu来提升权限,而不是sudo
  • entrypoint.sh脚本在执行的时候也是个进程,启动业务进程的时候,在命令前面加上exec,这样新的进程就会取代entrypoint.sh的进程,得到1号PID
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旺仔_牛奶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值