k8s_day03_01

docker-daemon 被拆分了好几个部分
- containerd: 高级容器运行时 :为用户使用更方便, 离用户更近、比较易用的命令行容器的管理工具但是包括docker bulid 之类的镜像管理工具【docker 当年捐献出来的只有容器运行时环境,而不包括镜像打包工具】
- runc: 低级容器运行时环境:
- containerd-shim, 是为了让runc 与oci 兼容的“垫片” 就是中间层。对接k8s CRI 接口,就是从containerd-shim 开始,而不是contained
podman 的好处:相比较docker ,docker 运行容器需要daemon ,而podman 不需要。podman的底层也是复用的runc, 所以podman当年docker 的技术: 当年docker 用的不是runc , 是lxc , 后来换成Libcontainer第三代才是containerd

pod 是容器集,是调度的最小单元。pod内部容器共享内部底层容器pause的3个名称空间 , 分别是network、ipc、UTS;pause的主要作用就是提供共享存储卷和名称空间,事实上也可以让pod 内容器共享pid 名称空间【默认是是不共享的,也可以打开pid共享,意味着pod内多个容器进程id pid 是共同编号的】,但是mount、 user 名称空间一般是让它门隔离的, 只是不会让他们共享,而不是不能
api版本的演进 。版本一共可以分成3个: alpha、beta、stable
例子: a
lpha 版本可以叫 v1alpha1 表示v1版本的内测阶段。所有的内测版本都不会随着发行版发行的,所以用命令 kubectl api-resources 是找不到alpha 版本的,因为我们作为客户端,能下载可用的话就已经是公开版本了,因此。。。
v1alpha1 也可以接着演进为 v1alpha2 直到为beta 版本 v1beta1 等等 最后走向 稳定版本v1 .
apiversion : GROUP/VERSION 。GROUP 可能不会改变,但是 version 可能会随时间变化。所以不同版本的k8 ,同一份配置清单 不一定都能 执行
github 上 Kubernetes v1.23.0-rc.0 rc 是release candidate 发行候选的简写,k8s最少3/4月,最多半年就会发布一个新版本
容器安全上下文
k8s 为了安全的运行容器,专门设计了一种 叫 pod 安全上下文的概念 Security Context 。 Security Context 主要是允许 用户和管理员 定义pod 或容器的特权 或访问控制机制的 。 以配置容器 主机以及其他容器之间的 隔离方式或者称之隔离级别。
人话: pod 安全上下文 就是一组用来决定容器如何创建或运行的约束条件。它们代表创建和运行容器运行时使用的运行时参数
pod 上的SC【Security Context】有2个级别
pod 级别:对pod内所有容器生效
容器级别:对pod内的单个容器生效
当然, 如果在集群级别,对所有pod 生效,用PSP: Pod Security Policy
配置格式速览
Security Context 常见配置
apiVersion: v1
kind: Pod
metadata: {…}
spec:
securityContext: # Pod级别的安全上下文,对内部所有容器均有效
runAsUser <integer> # 以指定的用户身份运行容器进程,默认由镜像中的USER指定
runAsGroup <integer> # 以指定的用户组运行容器进程,默认使用的组随容器运行时
supplementalGroups <[]integer> # 为容器中1号进程的用户添加的附加组;
fsGroup <integer> # 为容器中的1号进程附加的一个专用组,其功能类似于sgid
runAsNonRoot <boolean> # 是否以非root身份运行
seLinuxOptions <Object> # SELinux的相关配置
sysctls <[]Object> # 应用到当前Pod上的名称空间级别的sysctl参数设置列表
windowsOptions <Object> # Windows容器专用的设置
containers:
- name: …
image: …
securityContext: # 容器级别的安全上下文,仅生效于当前容器
runAsUser <integer> # 以指定的用户身份运行容器进程
runAsGroup <integer> # 以指定的用户组运行容器进程
runAsNonRoot <boolean> # 是否以非root身份运行
allowPrivilegeEscalation <boolean> # 是否允许特权升级
capabilities <Object> # 于当前容器上添加(add)或删除(drop)的内核能力
add <[]string> # 添加由列表定义的各内核能力
drop <[]string> # 移除由列表定义的各内核能力
privileged <boolean> # 是否运行为特权容器
procMount <string> # 设置容器的procMount类型,默认为DefaultProcMount;
readOnlyRootFilesystem <boolean> # 是否将根文件系统设置为只读模式
seLinuxOptions <Object> # SELinux的相关配置
windowsOptions <Object> # windows容器专用的设置
capabilities:
在Linux上 ,用户只有2中类型。管理员和普通用户。这样的分类方式太粗糙了。如果让一个普通用户能够管理主机的网络权限 ,但是又不能管理用户,怎么做?除了改sudo 就做不到了。对于 linux 来说 , 所有特权都是内核级别的,而且root 用户也是内核级别的,所以说root 天然具有特权权限,内核级权限 普通用户都没有,Linux就做了这种隔离。为了打破这种魔咒,linux 内核做了一种设定,把内核中的多个权限分成不同的组,分别用于不同类别的管理操作,而这每一个组所涉及的到的操作,我们就把它称为capabilities
, 比如管理网络的就叫做CapNetadmin 这个组,管理系统时钟的就叫 CapSystime 如果是拥有系统级管理员权限的就叫CapSysadmin 等等分了好多组 .
在容器中设置capabilities , 就是为了让容器具有 某些名称空间的特权。 add 添加某些内核能力, drop 关闭某些内核能力。 就是说可以让容器拥有内核级特权,管理内核的,但是只给它某些方面的特权而不是所有特权。比如说,正常情况下,普通用户是无法监听1024以内的端口的,如果给普通用户打开了netadmin 权限,用户就能就能监听80端口了。再比如,普通用户是无法管理内核的iptables 规则的,一旦给了他netadmin ,它就可以所在iptables 规则
privileged: 让容器运行为特权模式, 再次提醒特权一般只开放给k8s 系统级别的pod容器,应用容器是不必要或者一定不开启特权的 如 flannel 类型的组件就要开启为特权能力
procMount : 将宿主机的/proc 下系统级别的内核参数直接映射的容器中,,, 这个也不用管
readyonlyRootFilesystem : 设置之后,只能把数据写在存储卷上
管理容器运行身份
例子:
让容器内用户以普通用户身份执行
[root@master01 chapter4]# cat securitycontext-runasuser-demo.yaml
# Maintainer: MageEdu <mage@magedu.com>
# # URL: http://www.magedu.com
---
apiVersion: v1
kind: Pod
metadata:
name: securitycontext-runasuser-demo
namespace: default
spec:
containers:
- name: demo
image: ikubernetes/demoapp:v1.0
imagePullPolicy: IfNotPresent
env:
- name: PORT
value: "8080"
securityContext:
runAsUser: 1001
runAsGroup: 1001
验证结果
[root@master01 chapter4]# kubectl exec securitycontext-runasuser-demo -- ps aux
PID USER TIME COMMAND
1 1001 0:00 python3 /usr/local/bin/demo.py
17 1001 0:00 ps aux
[root@master01 chapter4]#
imagePullPolicy: 镜像拉取策略有3种
- never : 从不拉取。本地节点有镜像就用,没有就等着 Pending
- IfNotPresent: 如果把pod调度到该节点,如果节点上有镜像,就直接用,没有就拉取
- always: 即时本地节点有镜像,也要再次拉取【主要防止节点镜像被恶意攻击?】
imagePullPolicy默认值 取决于镜像的版本 。 如果有明确的版本 ,就是IfNotPresent , 如果是latest ,默认值就是always
管理容器内核功能/特权容器
例子:设定容器级别的capability
linux 内核从2.2 版本 开始支持附加于超级用户的管理权限 ,分割为多个独立的单元,每个单元就是capability.
以下是linux 内核常用的capability:
CAP_CHOWN:改变UID和GID;
CAP_MKNOD:mknod(),创建设备文件;
CAP_NET_ADMIN:网络管理权限;
CAP_SYS_ADMIN:大部分的管理权限; 生产环境给到这个权限就够了,就不要把容器 运行为特权模式了
CAP_SYS_TIME:
CAP_SYS_MODULE:装载卸载内核模块CAP_NET_BIND_SERVER:允许绑定特权端口【绑定1024以内的】
系统管理员可以直接通过getcap 、setcap 来设定权限
先来一个错误的案例
[root@master01 chapter4]# cat securitycontext-capabilities-demo.yaml
# Maintainer: MageEdu <mage@magedu.com>
# # URL: http://www.magedu.com
apiVersion: v1
kind: Pod
metadata:
name: securitycontext-capabilities-demo
namespace: default
spec:
containers:
- name: demo
image: ikubernetes/demoapp:v1.0
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c"]
args: ["/sbin/iptables -t nat -A PREROUTING -p tcp --dport 8080 -j REDIRECT --to-port 80 && /usr/bin/python3 /usr/local/bin/demo.py"]
# securityContext:
# capabilities:
# add: ['NET_ADMIN']
# drop: ['CHOWN']
[root@master01 chapter4]# kubectl apply -f securitycontext-capabilities-demo.yaml
pod/securitycontext-capabilities-demo created
[root@master01 chapter4]# kubectl get po/securitycontext-capabilities-demo
NAME READY STATUS RESTARTS AGE
securitycontext-capabilities-demo 0/1 CrashLoopBackOff 1 15s
describe pod 发现以下信息。
[root@master01 chapter4]# kubectl describe po/securitycontext-capabilities-demo
查看日志。 结果报错就是操作不允许
[root@master01 chapter4]# kubectl logs po/securitycontext-capabilities-demo
getsockopt failed strangely: Operation not permitted
为啥呢? 如果正常状态下 的 容器ps aux ,发现是会以root身份 运行pid =1 的 python3 /usr/local/bin/demo.py
容器内root的uid 也是为0 。问题?
因为容器内的root用户是被虚拟的,虽然看上去是以0号用户在运行,但是容器内的root 是被虚拟的,是映射的宿主机的普通用户而已,而宿主机的普通用户是无法设置iptables的, iptables 是内核级规则
command :
在容器上 ,可以用command 改变 容器中默认要运行的 程序, 就是在制作dockerfile 时可以用CMD或者entrypoint 指明的那个程序。 这里的command 起着类似的作用,前提是comand 所用到的程序 在镜像中必须存在
args: 指向自定义的程序传入参数。 args 其实都写在command 内也行
在正常情况下,容器是不具有特权权限的。因为k8s关闭了特权权限
容器上设置的iptables 和宿主机不一样,不是宿主机的,是容器的名称空间级别的规则
去了注释,加上 NET_ADMIN的capabilities就行了
apiVersion: v1
kind: Pod
metadata:
name: securitycontext-capabilities-demo
namespace: default
spec:
containers:
- name: demo
image: ikubernetes/demoapp:v1.0
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c"]
args: ["/sbin/iptables -t nat -A PREROUTING -p tcp --dport 8080 -j REDIRECT --to-port 80 && /usr/bin/python3 /usr/local/bin/demo.py"]
securityContext:
capabilities:
add: ['NET_ADMIN']
drop: ['CHOWN']
验证结果
默认情况下,容器的管理员root 是有权限 改容器内 任何文件的所属主组的。设置了drop chown就不行了
如下
[root@node01 ~]# kubectl exec securitycontext-capabilities-demo -- chown 1001 /etc/hosts
chown: /etc/hosts: Operation not permitted
command terminated with exit code 1
特权的pod, 以flannel 为例
原因在于kube-proxy的作用是 :
将apiserver 上定义的所有service 变动 ,映射为节点上的ipvs 或iptables规则
kube-proxy用到的功能操作都是内核级别的,且部分还超过了CAP_SYS_ADMIN。privileged 设置为true 后,就像是和宿主机上用户是一样的,如果是容器内以管理员的身份运行某个进程,像是在宿主机上以管理员用户执行一样,能直接操作宿主机内核
[root@node01 ~]# kubectl get po/kube-proxy-65zvn -n kube-system -o yaml
- /usr/local/bin/kube-proxy
- --config=/var/lib/kube-proxy/config.conf
- --hostname-override=$(NODE_NAME)
env:
- name: NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
image: registry.aliyuncs.com/google_containers/kube-proxy:v1.19.4
imagePullPolicy: IfNotPresent
name: kube-proxy
resources: {}
securityContext:
privileged: true
sysctls:
是让pod内的网络名称空间,可以直接独立的设定名称空间 级别 非 内核系统级别的参数,
事实上,目前在容器内,能允许pod 安全修改的参数只有3个:kernel.shm_rmid_forced, net.ipv4.ip_local_port_range, net.ipv4.tcp_syncookies。而且这3个参数的作用还不是特别的大 . 绝大多数参数是非安全参数,不允许在pod安全上下文内设定。了解就行👌
如果想让pod 内生效那些非安全参数 , 需要在k8s启动时设定 k8s支持非安全参数也允许。在pod内核中设定
方法如下:
在我们启动kubelet的时候,kubelet 有一个配置文件在/etc/default 目录下, 就叫kubelet, 如果不存在,自己建,kubelet 会到这里加载的
[root@node01 default]# cat kubelet
KUBELET_EXTRA_ARFS='--allowed-unsafe-sysctls=net.core.somaxconn,net.ipv4.ip_unprivileged_port_start'
net.core.somaxconn: 用于设定等待队列长度,系统级入站最大队列长度,默认是128
net.ipv4.ip_unprivileged_port_start 非特权用户可以使用端口的起始值,默认1024,必须重启kubelet,且必须每个节点都改,因为你不知道pod 以后会调度到哪个节点上去
例子: 让容器内普通用户监听80端口
[root@master01 chapter4]# cat securitycontext-sysctls-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: securitycontext-sysctls-demo
namespace: default
spec:
securityContext:
sysctls:
- name: kernel.shm_rmid_forced
value: "0"
- name: net.ipv4.ip_unprivileged_port_start
value: "0"
containers:
- name: demo
image: ikubernetes/demoapp:v1.0
imagePullPolicy: IfNotPresent
securityContext:
runAsUser: 1001
runAsGroup: 1001
[root@master01 chapter4]# kubectl get po/securitycontext-sysctls-demo
NAME READY STATUS RESTARTS AGE
securitycontext-sysctls-demo 0/1 SysctlForbidden 0 22s
[root@master01 chapter4]#
草?为啥改了kubelet 重启还不行😂