安全上下文(Security Context):K8s对Pod和容器提供的安全机制,可以设置Pod特权和访问控制。
背景:容器中的应用程序默认以root账号运行的,这个root与宿主机root账号是相同的,
拥有大部分对Linux内核的系统调用权限,
这样是不安全的,
所以我们应该将容器以普通用户运行,减少应用程序对权限的使用。
(容器是宿主机上面的进程,该进程有非常大的权限,容器只是在宿主机上面封装了状态,也是一个实际进程)
可以通过两种方法设置普通用户:
- Dockerfile里使用USER指定运行用户
- K8s里指定spec.securityContext.runAsUser,指定容器默认用户UID
[root@master ~]# cd flask-demo
[root@master flask-demo]# ls
Dockerfile main.py templates
#正常开发了网站程序,现在要将该网站程序容器化,第一步要写dockerfile制作镜像
#要基于某个镜像去制作镜像,一般基于java,或者操作系统centos
#下面是基于官方的python镜像去制作,也就是将程序打包到我的镜像,然后再到镜像准备python应用的环境
[root@master flask-demo]# ls
Dockerfile main.py templates
[root@master flask-demo]# cat Dockerfile
FROM python
#RUN useradd python
RUN mkdir /data/www -p
COPY . /data/www
#RUN chown -R python /data
RUN pip install flask -i https://mirrors.aliyun.com/pypi/simple/ && \
pip install prometheus_client -i https://mirrors.aliyun.com/pypi/simple/
WORKDIR /data/www
#USER python
CMD python main.py
现在将上面用户这些注释掉。先构造镜像将其跑起来
[root@master flask-demo]# docker build -t flask-demo .
[root@master flask-demo]# docker run -itd --name=demo -p 88:8080 flask-demo
66703f73e8e6f15b1cefe28c9c4f5dd65f3696a6382c79b7480e88df822bba94
[root@master flask-demo]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
66703f73e8e6 flask-demo "/bin/sh -c 'python ?? 6 seconds ago Up 5 seconds 0.0.0.0:88->8080/tcp, :::88->8080/tcp demo
[root@master flask-demo]# curl localhost:88
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>棣.〉</title>
</head>
<body>
<h1>Hello Python!</h1>
</body>
可以看到业务可以访问,在构造镜像的时候应该设置为普通用户,这样可以减少里面的应用对linux内核系统调用的权限,阻断一些异常的系统调用。
[root@master flask-demo]# docker exec -it demo bash
root@66703f73e8e6:/data/www# id
uid=0(root) gid=0(root) groups=0(root)
uid=0的就具有Linux内核所有的操作权限
[root@master flask-demo]# docker exec -it demo bash
root@66703f73e8e6:/data/www# id
uid=0(root) gid=0(root) groups=0(root)
root@66703f73e8e6:/data/www# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 14:18 pts/0 00:00:00 /bin/sh -c python main.py
root 7 1 0 14:18 pts/0 00:00:00 python main.py
root 9 0 0 14:26 pts/1 00:00:00 bash
root 16 9 0 14:28 pts/1 00:00:00 ps -ef
root@66703f73e8e6:/data/www# ls
Dockerfile main.py templates
root@66703f73e8e6:/data/www# ls -l
total 12
-rw-r--r-- 1 root root 297 Jul 6 14:15 Dockerfile
-rw-r--r-- 1 root root 200 Jul 2 02:35 main.py
drwxr-xr-x 2 root root 4096 Jul 2 02:17 templates
#可以看到可以安装工具
root@66703f73e8e6:/data/www# apt-get install wget
Reading package lists... Done
Building dependency tree
Reading state information... Done
wget is already the newest version (1.20.1-1.1).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
在dockerfile里面指定用户,那么宿主机上面就应该先有这个用户。
FROM python
RUN useradd python
RUN mkdir /data/www -p
COPY . /data/www
RUN chown -R python /data
RUN pip install flask -i https://mirrors.aliyun.com/pypi/simple/ && \
pip install prometheus_client -i https://mirrors.aliyun.com/pypi/simple/
WORKDIR /data/www
USER python
CMD python main.py
重新构建
[root@master flask-demo]# docker build -t flask-demo .
[root@master flask-demo]# docker run -itd --name=demo1 -p 87:8080 flask-demo
b658c8b614eb7b903d3f9cca0351a8a3a652b7166804a8b3336114abb9f654c3
[root@master flask-demo]# docker exec -it demo1 bash
python@b658c8b614eb:/data/www$ id
uid=1000(python) gid=1000(python) groups=1000(python)
python@b658c8b614eb:/data/www$ ps -ef
UID PID PPID C STIME TTY TIME CMD
python 1 0 0 14:43 pts/0 00:00:00 /bin/sh -c python main.py
python 7 1 0 14:43 pts/0 00:00:00 python main.py
python 8 0 0 14:43 pts/1 00:00:00 bash
python 16 8 0 14:43 pts/1 00:00:00 ps -ef
python@b658c8b614eb:/data/www$ ls -l
total 12
-rw-r--r-- 1 python root 294 Jul 6 14:41 Dockerfile
-rw-r--r-- 1 python root 200 Jul 2 02:35 main.py
drwxr-xr-x 1 python root 4096 Jul 2 02:17 templates
- RUN useradd python 先添加这个用户 USER python 这个用户必须系统当中包含,否则怎么帮你去设置这个用户?
- 当容器起来的时候,被禁锢在了python用户的环境下,python和root区别在于uid是不一样的,这样就在权限方面是不一样的,权限非常有限。
- 注意网站根目录也需要设置权限,要不然网站不能正常访问。
可以看到是以普通用户去运行镜像的。
python@b658c8b614eb:/data/www$ apt-get install wget
E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?
除了在dockefile里面去指定用户,还可以在securitycontext下面去指定。
案例1:设置容器以普通用户运行
背景:容器中的应用程序默认以root账号运行的,这个root与宿主机root账号是相同的, 拥有大部分对Linux内核的系统调用权限,这样是不安全的,所以我们应该将容器以普通用户运行,减少应用程序对权限的使用。
可以通过两种方法设置普通用户:
- Dockerfile里使用USER指定运行用户
- K8s里指定spec.securityContext.runAsUser,指定容器默认用户UID在这里只能指定ID,不能指定用户名了
[root@k8s-master ~]# cat flask-demo1.yaml
apiVersion: v1
kind: Pod
metadata:
name: flask-1
spec:
securityContext:
runAsUser: 1000 # 镜像里必须有这个用户UID
fsGroup: 1000 # 数据卷挂载后的目录属组设置为该组
containers:
- name: web
image: lizhenliang/flask-demo:root
volumeMounts:
- name: tmp
mountPath: /opt/data
securityContext:
allowPrivilegeEscalation: false # 不允许提权
volumes:
- name: tmp
emptyDir: {}
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
flask-1 1/1 Running 0 46s
[root@k8s-master ~]# kubectl exec -it flask-1 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
$ id
uid=1000(python) gid=1000(python) groups=1000(python)
[root@k8s-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
flask-1 1/1 Running 0 9s
[root@k8s-master ~]# kubectl exec -it flask-1 sh
$ ls -l /opt/data
total 0
$ ls -ld /opt/data
drwxrwsrwx 2 root python 6 Jul 8 20:17 /opt/data
$ id
uid=1000(python) gid=1000(python) groups=1000(python)
案例2:避免使用特权容器
背景:容器中有些应用程序可能需要访问宿主机设备、修改内核等需求,在默认情况下, 容器没这个有这个能力,因此这时会考虑给容器设置特权模式。
启用特权模式:
containers:
- image: lizhenliang/flask-demo:root
name: web
securityContext:
privileged: true
启用特权模式就意味着,你要为容器提供了访问Linux内核的所有能力,这是很危险的,为了减少系统调用的供给,可以使用Capabilities为容器赋予仅所需的能力。
Linux Capabilities:
Capabilities 是一个内核级别的权限,它允许对内核调用权限进行更细粒度的控制,而不是简单地以 root 身份能力授权。
Capabilities 包括更改文件权限、控制网络子系统和执行系统管理等功能。在securityContext 中,可以添加或删除 Capabilities,做到容器精细化权限控制。
securityContext:
allowPrivilegeEscalation: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
# www-data -> 101
runAsUser: 101