权限分析
- Docker容器运行的时候,如果没有专门指定user, 默认以root用户运行。我们镜像里没有指定user,容器里的执行用户的id是0,输出文件的权限也是0
[root@boy dockerfile]
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@boy dockerfile]
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账号运行进程才是安全的方式,这也是我们在制作镜像时要注意的地方
su 和sudo 具有非常奇怪且经常令人讨厌的TTY和信号转发行为的问题。su 和sudo 的设置和使用也有些复杂(特别是在sudo 的情况下),虽然它们有很大的表达力,但是如果您所需要的只是“以特定用户身份运行特定应用程序”,那么它们将不再那么适合- 处理完用户/组后,我们将切换到指定用户,然后执行指定的进程,gosu本身不再驻留或完全不在进程生命周期中。这避免了信号传递和TTY的所有问题
- 不要在容器中使用
sudo 命令,因为sudo的执行机制问题,如下所示,我们在启动容器时执行了sudo ps -ef 命令,发现我们命名只执行了一条命令,但是竟然会有2个进程,请注意PID ,真正执行ps -ef 的命令的PID是7 ,而不是1 ,这回导致当前进程无法接受Unix的SIGNAL
[root@jiayu ~]
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
[root@jiayu ~]
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
[root@jiayu ~]
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
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"]
set -e
if [ "${1#-}" != "$1" ] || [ "${1%.conf}" != "$1" ]; then
set -- redis-server "$@"
fi
if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
find . \! -user redis -exec chown redis '{}' +
exec gosu redis "$0" "$@"
fi
exec "$@"
注意上面的代码,我们来分析一下:
- 假设启动容器的命令是docker run -it redis redis-server /etc/redis.conf
- 容器启动后会执行docker-entrypoint.sh脚本,此时的账号是root,此时对于容器来说接收到命令为:
docker-entrypoint.sh redis-server /etc/redis.conf - exec gosu redis $0 $@
docker-entrypoint.sh redis-server /etc/redis.conf
替换当前的shell,且不会生成新的进程保证了gosu redis “$0” "@"对应的进程ID为1
此时运行命令为:docker-entrypoint.sh redis-server /etc/redis.conf
- 当切换成redis用户后会执行如下命令
redis-server /etc/redis.conf
gosu实例
[root@boy 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]
if [ "$(id -u)" == "0" ]; then
exec gosu test "$0" "$@"
fi
exec $@
[root@boy dockerfile]
[root@boy dockerfile]
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
|